package com.dji.ux.sample;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;

import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.location.Location;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.alibaba.fastjson.JSON;
import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.MyLocationStyle;
import com.dji.mapkit.core.maps.DJIMap;
import com.dji.mapkit.core.models.DJILatLng;

import com.dji.ux.sample.Adapter.PanoTaskBean;
import com.dji.ux.sample.CustomView.AISurfaceViewManager;
import com.dji.ux.sample.MQSignalClient.MQSignalClient;
import com.dji.ux.sample.MediaManager.MediaEvent;
import com.dji.ux.sample.MediaManager.MediaUploadUtil;
import com.dji.ux.sample.MediaManager.MediaUtil;
import com.dji.ux.sample.MediaManager.PanoTaskPanel;
import com.dji.ux.sample.TCPClient.PLCStatus;
import com.dji.ux.sample.TCPClient.TCPDrone;
import com.dji.ux.sample.Utils.CrashHandler;
import com.dji.ux.sample.Utils.DensityUtil;
import com.dji.ux.sample.Utils.ExcelDataUtil;
import com.dji.ux.sample.Utils.GPSUtil;
import com.dji.ux.sample.Utils.GimbalUtil;
import com.dji.ux.sample.Utils.JSONUtil;
import com.dji.ux.sample.Utils.SimpleUtils;
import com.dji.ux.sample.Utils.ToastUtils;
import com.dji.ux.sample.Utils.ToolUtil;
import com.dji.ux.sample.Utils.WayPointNode;
import com.dji.ux.sample.VirtualSticks.UrgencyLandController;
import com.dji.ux.sample.VirtualSticks.VirtualStickData;
import com.dji.ux.sample.VirtualSticks.Panorama.VirtualStickPanoramaController;
import com.dji.ux.sample.VirtualSticks.VirtualSticksController;
import com.dji.ux.sample.MECSignalClient.IReceiveMessage;
import com.dji.ux.sample.MECSignalClient.WebSocketManager;
import com.dji.ux.sample.RTCSignalClient.MyIceServer;
import com.dji.ux.sample.RTCSignalClient.RTCSignalClient;
import com.dji.ux.sample.Weather.WeatherParams;
import com.dji.videostreamdecodingsample.media.DJIVideoStreamDecoder;
import com.dji.videostreamdecodingsample.media.NativeHelper;
import com.google.gson.Gson;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.Logging;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.ScreenCapturerAndroid;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoDecoderFactory;
import org.webrtc.VideoEncoderFactory;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.xutils.common.Callback;
import org.xutils.http.RequestParams;
import org.xutils.x;

import java.io.ByteArrayOutputStream;

import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import dji.common.battery.BatteryState;
import dji.common.camera.OriginalPhotoSettings;
import dji.common.camera.SettingsDefinitions;
import dji.common.error.DJIError;
import dji.common.error.DJISDKError;
import dji.common.flightcontroller.GoHomeExecutionState;
import dji.common.flightcontroller.virtualstick.FlightCoordinateSystem;
import dji.common.flightcontroller.virtualstick.RollPitchControlMode;
import dji.common.flightcontroller.virtualstick.VerticalControlMode;
import dji.common.flightcontroller.virtualstick.YawControlMode;

import dji.common.mission.waypoint.Waypoint;
import dji.common.mission.waypoint.WaypointAction;
import dji.common.mission.waypoint.WaypointActionType;
import dji.common.mission.waypoint.WaypointMission;
import dji.common.mission.waypoint.WaypointMissionDownloadEvent;
import dji.common.mission.waypoint.WaypointMissionExecuteState;
import dji.common.mission.waypoint.WaypointMissionExecutionEvent;
import dji.common.mission.waypoint.WaypointMissionFinishedAction;
import dji.common.mission.waypoint.WaypointMissionFlightPathMode;
import dji.common.mission.waypoint.WaypointMissionHeadingMode;
import dji.common.mission.waypoint.WaypointMissionState;
import dji.common.mission.waypoint.WaypointMissionUploadEvent;
import dji.common.model.LocationCoordinate2D;
import dji.common.product.Model;
import dji.common.util.CommonCallbacks;
import dji.keysdk.CameraKey;
import dji.keysdk.KeyManager;
import dji.log.DJILog;
import dji.sdk.base.BaseComponent;
import dji.sdk.base.BaseProduct;
import dji.sdk.battery.Battery;
import dji.sdk.camera.Camera;
import dji.sdk.camera.Lens;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.codec.DJICodecManager;
import dji.sdk.flightcontroller.FlightController;
import dji.sdk.mission.MissionControl;
import dji.sdk.mission.waypoint.WaypointMissionOperator;
import dji.sdk.mission.waypoint.WaypointMissionOperatorListener;
import dji.sdk.products.Aircraft;
import dji.sdk.sdkmanager.DJISDKInitEvent;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.sdkmanager.LiveStreamManager;

import dji.common.airlink.PhysicalSource;
import dji.sdk.airlink.OcuSyncLink;
import dji.sdk.sdkmanager.LiveVideoBitRateMode;
import dji.sdk.sdkmanager.LiveVideoResolution;
import dji.sdksharedlib.hardware.abstractions.remotecontroller.V;
import dji.thirdparty.afinal.core.AsyncTask;

import dji.ux.widget.MapWidget;
import dji.ux.widget.controls.CameraControlsWidget;
import okio.ByteString;

import static java.lang.Thread.sleep;

/**
 * Activity that shows all the UI elements together
 */
public class CompleteWidgetActivity extends AppCompatActivity implements IReceiveMessage, DJICodecManager.YuvDataCallback {

    private static final String TAG = CompleteWidgetActivity.class.getName();

    private MapWidget mapWidget;
    private ViewGroup parentView;
    private boolean isMapMini = true;

    private int height;
    private int width;
    private int margin;
    private int deviceWidth;
    private int deviceHeight;

    private DJIMap djiMap;
    private AMap aMap;
    private FlightController mFlightController;
    private float curr_hight = 30.0f;
    private double droneLocationLat;
    private double droneLocationLng;

    private DJILatLng homepoint;

    private TextView GPS_panel;
    private Button show_GPS;
    private Button set_homepoint;
    private Button btn_connect_websocket;
    private Button btn_show_funPanel;
    private GridLayout buttonList;

    public VirtualSticksController mvirtualSticksController;
    private Location currLocation;
    private DJILatLng current_latlng;
    private LatLng curr_amapLatlng;

    private SurfaceHolder.Callback surfaceCallback;
    private VideoFeeder.VideoFeed standardVideoFeeder;
    protected VideoFeeder.VideoDataListener mReceivedVideoDataListener = null;

    private TextureView videostreamPreviewTtView;
    private SurfaceView videostreamPreviewSf;
    private SurfaceHolder videostreamPreviewSh;

    private DJICodecManager mCodecManager;
    private TextView savePath;
    private Button screenShot;
    private int videoViewWidth;
    private int videoViewHeight;
    private int count;
    public static int waypointcount = 0;
    public static int shootcount = -1;
    private Camera mCamera;
    private StringBuilder stringBuilder;

    //    private String rtmp_server = "rtmp://8.134.15.236:1935/live/";
//    private String rtmp_server = "rtmp://192.168.2.124:1935/live/";
    static public String localIp = "8.134.15.236";
    private String rtmp_server = "rtmp://" + localIp + ":1935/live/";
    private String rtmp_url = "";
    private String websocketIP = "";
    private LiveStreamManager.OnLiveChangeListener listener;
    private Button liveStream_switch;

    /**
     * 保存服务器地址设置数据文件的名字
     */
    private String ServerAddressList = "ServerAdds";
    private String websocket_server = "ws_address";
    private String rtmp_address = "rtmp_address";
    private SharedPreferences sharedPreferences;

    private SharedPreferences urgencyLandSP;
    private UrgencyLandController murgencyLandController;


    /**
     * 实时显示经纬度、方向角
     */
    private double fxj_heading_cur;
    private int fxj2;

    private TextView tv_longitude;
    private TextView tv_latitude;
    private TextView tv_directionAngle;

    /**
     * waypoint标记
     */
    private ArrayList<Marker> Markers = new ArrayList<>();
    private DJILatLng curMarker;
    /**
     * waypoint列表
     */
    private List<Waypoint> waypointList = new ArrayList<>();
    private List<Waypoint> swaypointList = new ArrayList<>();
    public static WaypointMission.Builder waypointMissionBuilder;
    /**
     * Waypoint任务完成之后的操作
     */
    private WaypointMissionFinishedAction mFinishedAction = WaypointMissionFinishedAction.NO_ACTION;
    /**
     * Waypoint镜头朝向
     */
    private WaypointMissionHeadingMode mHeadingMode = WaypointMissionHeadingMode.AUTO;

    private WaypointMissionOperator waypointMissionOperator;

    private EditText B_lat;
    private EditText B_lng;
    private Button waypoint_conf;
    private Button start_waypoint_mission;

    /**
     * 添加Waypoint开关
     */
    private boolean isAdd = false;
    private Button addwaypoint_btn;

    private float altitude = 100.0f;
    private float mSpeed = 10.0f;

    /**
     * 添加历史任务变量
     */
    private String recordName = "Mavic2-20210624";
    private String recordToken = "J2y+HhakfSSznrLmmEoQncxWACOANVN7CuOd6w+2PJgb/w4aa767+6DaJSKIJdKl";
    private int recordId = 9;

    /**
     * 添加AB点测试Waypoint变量
     */
    private DJILatLng a_point;
    private DJILatLng b_point;

    /**
     * 添加WebRTC变量
     */

    private static int VIDEO_RESOLUTION_WIDTH = 1920;
    private static int VIDEO_RESOLUTION_HEIGHT = 1080;
    private static final int VIDEO_FPS = 30;

    public static final String VIDEO_TRACK_ID = "ARDAMSv0";
    public static final String AUDIO_TRACK_ID = "ARDAMSa0";

    private EglBase mRootEglBase;

    private PeerConnection mPeerConnection; // 类似js的RTCPeerConnection
    private PeerConnectionFactory mPeerConnectionFactory;

    private SurfaceTextureHelper mSurfaceTextureHelper;
    private VideoTrack mVideoTrack;
    private AudioTrack mAudioTrack;

    private VideoCapturer mVideoCapturer;
    private ExecutorService mExecutor;

    private ArrayList<PeerConnection.IceServer> ICEServers;
    // turn and stun
    private static MyIceServer[] iceServers = {

            new MyIceServer("stun:" + localIp + ":3478"),
            new MyIceServer("turn:" + localIp + ":3478?transport=udp",
                    "admin",
                    "admin"),

            new MyIceServer("turn:" + localIp + ":3478?transport=tcp",
                    "admin",
                    "admin"),
            new MyIceServer("turn:" + localIp + ":3478",
                    "admin",
                    "admin")
    };
    private RTCSignalClient mSignalClient = null;
    private MediaProjectionManager projectionManager;
    private static final int REQUEST_CODE = 102;

    private String mRemoteUserId;
    private String mRoomId = "08RDE9M00102DB";
    private String mUserID = "android";
    private String mMultLiveId = "";
    private boolean mIsJoinRoom = false;
    private TextView mLogcatView;

    private JSONObject recordJSONObject = null;

    private String product_name;

    /**
     * MQ Client
     */
    private MQSignalClient wpMQRequestHandler;
    private MQSignalClient wpMQResponseHandler;


    /**
     * 添加电池管理变量
     */
    private Battery mBatteryInstance;
    private int batteryRemainInPercent;

    private DecimalFormat flightdataFormat = new DecimalFormat(".00");

    private boolean isMissionReady = false;
    public boolean isLandReady = false;
    private boolean isLanding = false;
    public boolean isTakeOff = false;
    private boolean isTakeOffFinish = false;
    private boolean isMissionFinish = false;
    private boolean isAdjustHeading = false;

    private SurfaceView mAISurfaceView;
    private SurfaceHolder mAISurfaceHolder;
    private AISurfaceViewManager mAISurfaceViewManager;
    private TCPDrone tcpDrone;
    private ArrayList<PLCStatus> plcStatusesList = new ArrayList<PLCStatus>();

    private WeatherParams weatherParams;

    private boolean LandInspect;
//    public static boolean needupload;

    private PanoTaskPanel mPanoTaskPanel;
    private Button show_pano;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        socketClient.connect();
        getWindow().addFlags(
                WindowManager.LayoutParams.FLAG_FULLSCREEN
                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        setContentView(R.layout.activity_default_widgets);
        initUI(savedInstanceState);

        if (isM300Product()) {
            OcuSyncLink ocuSyncLink = DJISampleApplication.getProductInstance().getAirLink().getOcuSyncLink();
            // If your MutltipleLensCamera is set at right or top, you need to change the PhysicalSource to RIGHT_CAM or TOP_CAM.
            ocuSyncLink.assignSourceToPrimaryChannel(PhysicalSource.LEFT_CAM, PhysicalSource.FPV_CAM, new CommonCallbacks.CompletionCallback() {
                @Override
                public void onResult(DJIError error) {
                    if (error == null) {
                        showToast("assignSourceToPrimaryChannel success.");
                    } else {
                        showToast("assignSourceToPrimaryChannel fail, reason: " + error.getDescription());
                    }
                }
            });
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        }

        mExecutor = Executors.newSingleThreadExecutor();

        mSignalClient = new RTCSignalClient(mOnSignalEventListener);    // 创建信令


        String requestQueue = "uav_app_request";
        wpMQRequestHandler = new MQSignalClient(requestQueue, new MQSignalHandler(requestQueue));
        wpMQRequestHandler.startReceiveMQ();


        height = DensityUtil.dip2px(this, 100);
        width = DensityUtil.dip2px(this, 150);
        margin = DensityUtil.dip2px(this, 55);

        WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        final Display display = windowManager.getDefaultDisplay();
        Point outPoint = new Point();
        display.getRealSize(outPoint);
        deviceHeight = outPoint.y;
        deviceWidth = outPoint.x;

        //保存设置数据
        sharedPreferences = getSharedPreferences(ServerAddressList, Activity.MODE_PRIVATE);
        urgencyLandSP = getSharedPreferences("urgencyLand", Activity.MODE_PRIVATE);
        murgencyLandController = new UrgencyLandController(CompleteWidgetActivity.this, urgencyLandSP);

        // The callback for receiving the raw H264 video data for camera live view
        initSurfaceOrTextureView();
        initListener();

//        initTCPClient();
//        initUDPClient();
//        mvirtualSticksController = new VirtualSticksController(CompleteWidgetActivity.this, sharedPreferences);
        CrashHandler.getInstance().init(CompleteWidgetActivity.this);
    }

    public static boolean isM300Product() {
        if (DJISDKManager.getInstance().getProduct() == null) {
            return false;
        }
        Model model = DJISDKManager.getInstance().getProduct().getModel();
        return model == Model.MATRICE_300_RTK;
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerSDK();
        initFlightController();
        notifyStatusChange();
        MediaUtil.setMediaUpdateListener(CompleteWidgetActivity.this, mediaEventCallBack);


        // Hide both the navigation bar and the status bar.
        View decorView = getWindow().getDecorView();
        decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

        mapWidget.onResume();
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        Intent captureIntent = projectionManager.createScreenCaptureIntent();
        startActivityForResult(captureIntent, REQUEST_CODE);
        BaseProduct product = DJISampleApplication.getProductInstance();
        if (product == null || !product.isConnected()) {
            ToastUtils.showShort(CompleteWidgetActivity.this, "Disconnect");
            return;
        }
        if (isLiveStreamManagerOn()) {
            DJISDKManager.getInstance().getLiveStreamManager().registerListener(listener);
        }

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @org.jetbrains.annotations.Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        ToastUtils.showShort(CompleteWidgetActivity.this, "REQUEST_CODE" + requestCode + "," + resultCode);

        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
            mVideoCapturer = new ScreenCapturerAndroid(data, new MediaProjection.Callback() {
                @Override
                public void onStop() {
                    super.onStop();
                }
            });
            onScreenCreate();
        }
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (isLiveStreamManagerOn()) {
            DJISDKManager.getInstance().getLiveStreamManager().unregisterListener(listener);
        }
    }

    @Override
    protected void onPause() {
        mapWidget.onPause();
        if (mCamera != null) {
            if (VideoFeeder.getInstance().getPrimaryVideoFeed() != null) {
                VideoFeeder.getInstance().getPrimaryVideoFeed().removeVideoDataListener(mReceivedVideoDataListener);
            }
            if (standardVideoFeeder != null) {
                standardVideoFeeder.removeVideoDataListener(mReceivedVideoDataListener);
            }
        }
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mCodecManager != null) {
            mCodecManager.cleanSurface();
            mCodecManager.destroyCodec();
        }
        mapWidget.onDestroy();
        WebSocketManager.getInstance().close();
        if (mvirtualSticksController != null) {
            mvirtualSticksController.closeVirtualStickController();
        }
        removeListener();
        stopWaypointMission();
        if (mIsJoinRoom) {
            mIsJoinRoom = false;
            doEndCall();
        }
        try {
            if (mVideoCapturer != null) {
                mVideoCapturer.stopCapture();
                mVideoCapturer.dispose();
            }
            if (mSurfaceTextureHelper != null) mSurfaceTextureHelper.dispose();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        PeerConnectionFactory.stopInternalTracingCapture();
        PeerConnectionFactory.shutdownInternalTracer();

        mSignalClient.disConnect();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapWidget.onSaveInstanceState(outState);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapWidget.onLowMemory();
    }

    private void onScreenCreate() {
        mRootEglBase = EglBase.create();
        //创建 factory， pc是从factory里获得的
        mPeerConnectionFactory = createPeerConnectionFactory(this);
        // NOTE: this _must_ happen while PeerConnectionFactory is alive!
        Logging.enableLogToDebugOutput(Logging.Severity.LS_VERBOSE);
        /**
         * 纹理渲染
         */
        mSurfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", mRootEglBase.getEglBaseContext());

        /**
         * 创建视频源，isScreencast：是否是投屏
         */
        VideoSource videoSource = mPeerConnectionFactory.createVideoSource(false);
        //传递视频信息
        mVideoCapturer.initialize(mSurfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
        mVideoTrack = mPeerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
        mVideoTrack.setEnabled(true);

        /**
         * 创建音屏源
         */
        MediaConstraints mediaConstraints = new MediaConstraints();
        // 添加所有现有的音频过滤器，以避免回声
        //回声消除
        mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));

        AudioSource audioSource = mPeerConnectionFactory.createAudioSource(mediaConstraints);
        mAudioTrack = mPeerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
        mAudioTrack.setEnabled(false);

        mVideoCapturer.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, VIDEO_FPS);
    }

    public PeerConnectionFactory createPeerConnectionFactory(Context context) {
        final VideoEncoderFactory encoderFactory;
        final VideoDecoderFactory decoderFactory;

        encoderFactory = new DefaultVideoEncoderFactory(
                mRootEglBase.getEglBaseContext(), false /* enableIntelVp8Encoder */, true);
        decoderFactory = new DefaultVideoDecoderFactory(mRootEglBase.getEglBaseContext());

        PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(context)
                .setEnableInternalTracer(true)
                .createInitializationOptions());

        PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder()
                .setVideoEncoderFactory(encoderFactory)
                .setVideoDecoderFactory(decoderFactory);
        builder.setOptions(null);

        return builder.createPeerConnectionFactory();
    }

    private PeerConnection.Observer mPeerConnectionObserver = new PeerConnection.Observer() {
        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.i(TAG, "onSignalingChange: " + signalingState);
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            Log.i(TAG, "onIceConnectionChange: " + iceConnectionState);
        }

        @Override
        public void onIceConnectionReceivingChange(boolean b) {
            Log.i(TAG, "onIceConnectionChange: " + b);
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.i(TAG, "onIceGatheringChange: " + iceGatheringState);
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            Log.i(TAG, "onIceCandidate: " + iceCandidate);

            mSignalClient.sendCandidate(toJsonCandidate(iceCandidate), mRemoteUserId);
        }

        @Override
        public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
            for (int i = 0; i < iceCandidates.length; i++) {
                Log.i(TAG, "onIceCandidatesRemoved: " + iceCandidates[i]);
            }
            mPeerConnection.removeIceCandidates(iceCandidates);
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.i(TAG, "onAddStream: " + mediaStream.videoTracks.size());
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
            Log.i(TAG, "onRemoveStream");
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
            Log.i(TAG, "onDataChannel");
        }

        @Override
        public void onRenegotiationNeeded() {
            Log.i(TAG, "onRenegotiationNeeded");
        }

        @Override
        public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
        }
    };

    private class MQSignalHandler implements MQSignalClient.MQEventListener {
        private String qname;

        private MQSignalHandler(String queueName) {
            qname = queueName;
        }

        @Override
        public void onMessage(long deliveryTag, JSONObject message) {
            logcatOnUI(String.format("%s MQ msg: %s, tag: %s", qname, message.toString(), deliveryTag));
            try {
                waypointList.clear();
                waypointList.add(new Waypoint(current_latlng.latitude, current_latlng.longitude, altitude));
                mFinishedAction = JSONUtil.getFinishAction(message.getInt("finishedAction"));
                mHeadingMode = JSONUtil.getHeadMode(message.getInt("heading"));
                mSpeed = JSONUtil.getSpeed(message.getString("speed"));
                altitude = JSONUtil.getAltitude(message.getString("altitude"));
                MediaUploadUtil.token1 = message.getString("token");
                Log.d(TAG, "token: " + message.getString("token"));
                //国土查违部分
                List<WayPointNode> wayPointNodes = JSON.parseArray(message.getString("wayPointList"), WayPointNode.class);
                List<String> nodeIdList = new ArrayList<>();
                Log.d(TAG, "onWaypointDataRcv: " + wayPointNodes.toString());

                List<PanoTaskBean> pano_data = new ArrayList<>();
                for (int node_i = 0; node_i < wayPointNodes.size(); node_i++) {
                    nodeIdList.add(wayPointNodes.get(node_i).nodeId);
                    PanoTaskBean ptb = new PanoTaskBean();
                    ptb.setId(wayPointNodes.get(node_i).nodeId);
                    pano_data.add(ptb);
                    double[] point = GPSUtil.gcj02_To_Gps84(wayPointNodes.get(node_i).point[1], wayPointNodes.get(node_i).point[0]);
                    waypointList.add(new Waypoint(point[0], point[1], altitude));
                }
                runOnUiThread(() -> mPanoTaskPanel.refreshData(pano_data));

                Log.d(TAG, "onWaypointDataRcv: nodeIdList" + nodeIdList.toString());
                Log.d(TAG, "onWaypointDataRcv: waypointList" + waypointList.toString());
                MediaUploadUtil.prepareUploadData(CompleteWidgetActivity.this, nodeIdList, uploadListener);
                MediaUtil.setNodeIDs(nodeIdList);
                waypointcount = 0;
                shootcount = -1;
                configWayPointMission(true);
            } catch (Exception e) {
                logcatOnUI(String.format("%s MQ error: %s", qname, e.toString()));
                e.printStackTrace();
            }

        }

        @Override
        public void onError(String error) {
            logcatOnUI(String.format("%s MQ onMessage error: %s", qname, error));
        }
    }

    private RTCSignalClient.OnSignalEventListener mOnSignalEventListener = new RTCSignalClient.OnSignalEventListener() {
        @Override
        public void onConnected() {
            logcatOnUI("Signal Server Connected !");
            new Thread(() -> {
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mSignalClient.joinRoom(mUserID, mRoomId);
                mIsJoinRoom = true;
            }).start();
        }

        @Override
        public void onConnecting() {
            logcatOnUI("Signal Server Connecting !");
        }

        @Override
        public void onJoinRoomFailure() {
            mSignalClient = new RTCSignalClient(mOnSignalEventListener);    // 创建信令
            logcatOnUI("join room failure, reconnecting now");
        }

        @Override
        public void onReconnecting() {
            logcatOnUI("Signal Server Reconnecting !");
        }

        @Override
        public void onDisconnected() {
            logcatOnUI("Signal Server Disconnected");
        }

        @Override
        public void onClosse() {
            logcatOnUI("Signal Server close");
        }


        @Override
        public void onRemoteNewPeer(JSONObject message) {
            try {
                mRemoteUserId = message.getString("remoteUid");
            } catch (JSONException e) {
                Log.e(TAG, "onRemoteNewPeer JSON parsing error: " + e.toString());
            }

            logcatOnUI("Remote User Joined: " + mRemoteUserId);
            mExecutor.execute(() -> {
                doStartCall();
            });
        }

        @Override
        public void onResponseJoin(JSONObject message) {
            try {
                mRemoteUserId = message.getString("remoteUid");
            } catch (JSONException e) {
                Log.e(TAG, "onResponseJoin JSON parsing error: " + e.toString());
            }

            logcatOnUI("Receive Remote User Joined");
        }

        @Override
        public void onRemotePeerLeave(JSONObject message) {
            logcatOnUI("Receive Remote Hanup  ...");
            hanup();
        }

        @Override
        public void onRemoteOffer(JSONObject message) {
            logcatOnUI("Receive Remote Offer ...");
            if (mPeerConnection == null) {
                mPeerConnection = createPeerConnection();
            }
            try {
                JSONObject sdpJson = new JSONObject(message.getString("msg"));
                String description = sdpJson.getString("sdp");
                doAnswerCall(description);

            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onRemoteAnswer(JSONObject message) {
            logcatOnUI("Receive Remote Answer ...");
            try {
                JSONObject sdpJson = new JSONObject(message.getString("msg"));
                String description = sdpJson.getString("sdp");
                mPeerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(SessionDescription.Type.ANSWER, description));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onRemoteCandidate(JSONObject message) {
            logcatOnUI("Receive Remote Candidate ...");
            try {
                String candidateString = message.getString("msg");
                IceCandidate iceCandidate = toJavaCandidate(new JSONObject(candidateString));
                mPeerConnection.addIceCandidate(iceCandidate);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onControlDataRcv(JSONObject message) {
            try {
                String controlString = message.toString();
                //mvirtualSticksController.decodeControllData(controlString);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onWaypointDataRcv(JSONObject message) {
            logcatOnUI("Receive waypoint data ...");
            try {
                waypointList.clear();

                waypointList.add(new Waypoint(current_latlng.latitude, current_latlng.longitude, altitude));
                mFinishedAction = JSONUtil.getFinishAction(message.getInt("finishedAction"));
                mHeadingMode = JSONUtil.getHeadMode(message.getInt("heading"));
                mSpeed = JSONUtil.getSpeed(message.getString("speed"));
                altitude = JSONUtil.getAltitude(message.getString("altitude"));
                recordName = mRoomId + "-" + message.getString("recordName").split("-", 2)[1];
                Log.i(TAG, "recordName: " + recordName);
                recordToken = message.getString("token");
                //原航点任务加载
                String waypoints = message.getString("wayPointList")
                        .replace("[", "").replace("]", "").replace("\"", "");
                String[] strWaypoints = waypoints.split(",");
                int waypoint_count = 1;
                Log.i("RecWayPointData", message.toString());
                for (int waypoint_i = 0; waypoint_i < strWaypoints.length; waypoint_i++) {
                    Log.i(TAG, waypoint_count + " onWaypointRcv: " + strWaypoints[waypoint_i]);
                    if (waypoint_count % 2 == 0) {
                        double[] djilatlng = GPSUtil.gcj02_To_Gps84(
                                Double.parseDouble(strWaypoints[waypoint_i]),
                                Double.parseDouble(strWaypoints[waypoint_i - 1]));
                        Log.i(TAG, "onWaypointDataRcv djiLatlng: " + djilatlng[0] + "---" + djilatlng[1]);
                        waypointList.add(new Waypoint(djilatlng[0], djilatlng[1], altitude));
                    }
                    waypoint_count++;
                }
                waypointcount = 0;

                getWaypointMissionOperator().clearMission();
                configWayPointMission(false);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onLandInspectionRcv(JSONObject message) {
            try {
                waypointList.clear();
                waypointList.add(new Waypoint(current_latlng.latitude, current_latlng.longitude, altitude));
                mFinishedAction = JSONUtil.getFinishAction(message.getInt("finishedAction"));
                mHeadingMode = JSONUtil.getHeadMode(message.getInt("heading"));
                mSpeed = JSONUtil.getSpeed(message.getString("speed"));
                altitude = JSONUtil.getAltitude(message.getString("altitude"));
                MediaUploadUtil.token1 = message.getString("token");
                Log.d(TAG, "token: " + message.getString("token"));
                //国土查违部分
                List<WayPointNode> wayPointNodes = JSON.parseArray(message.getString("wayPointList"), WayPointNode.class);
                List<String> nodeIdList = new ArrayList<>();
                Log.d(TAG, "onWaypointDataRcv: " + wayPointNodes.toString());

                List<PanoTaskBean> pano_data = new ArrayList<>();
                for (int node_i = 0; node_i < wayPointNodes.size(); node_i++) {
                    nodeIdList.add(wayPointNodes.get(node_i).nodeId);
                    PanoTaskBean ptb = new PanoTaskBean();
                    ptb.setId(wayPointNodes.get(node_i).nodeId);
                    pano_data.add(ptb);
                    double[] point = GPSUtil.gcj02_To_Gps84(wayPointNodes.get(node_i).point[1], wayPointNodes.get(node_i).point[0]);
                    waypointList.add(new Waypoint(point[0], point[1], altitude));
                }
                mPanoTaskPanel.refreshData(pano_data);

                Log.d(TAG, "onWaypointDataRcv: nodeIdList" + nodeIdList.toString());
                Log.d(TAG, "onWaypointDataRcv: waypointList" + waypointList.toString());
                MediaUploadUtil.prepareUploadData(CompleteWidgetActivity.this, nodeIdList, uploadListener);
                MediaUtil.setNodeIDs(nodeIdList);
                waypointcount = 0;
                shootcount = -1;
                configWayPointMission(true);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onStartWaypoint(JSONObject message) {
            try {
                mSpeed = Float.parseFloat(message.getString("speed"));

            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onPlcDataRcv(JSONObject message) {
            try {
                String plcData = message.getString("msg");
                switch (plcData) {
                    case "SIGNAL_TYPE_PLCDATA":
                        /*
                        new Thread(() -> {
                            tcpDrone.sendCommand(PLCCommand.drone_power);
                            Timer timer = new Timer();
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    while (true) {
                                        if (havePLCStatus(PLCStatus.DRONE_POWER_OK)) {
                                            tcpDrone.sendCommand(PLCCommand.ready_takeOff);
                                            break;
                                        }
                                    }
                                }
                            }, 5000);
                        }).start();
                        logcatOnUI("TURN_AIRCRAFT_ON");
                        */
                        break;
                    default:
                        Log.e(TAG, "can't handle the cmd " + plcData);
                        break;
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onTakeOffRcv(JSONObject message) {
            mvirtualSticksController.Aircraft_takeoff();
        }

        @Override
        public void onLandRcv(JSONObject message) {
            isLanding = true;
            mvirtualSticksController.stopRotation_mission();
            mvirtualSticksController.setlX(0);
            mvirtualSticksController.setlY(0);
            try {
                GimbalUtil.adjustGimbalPitch(-90);
                start_waypoint_mission.setText("开始任务");
                Button vt_btn = findViewById(R.id.endisable_virturalSticks);
                if (vt_btn.getText().equals("开启摇杆")) {
                    mvirtualSticksController.enableVirtualSticks();
                    vt_btn.setText("关闭摇杆");
                }
                startLiveShow(recordName);
            } catch (Exception e) {
                e.printStackTrace();
            }
            isMissionFinish = true;
        }

        @Override
        public void onGimbalDataRcv(JSONObject message) {
            try {
                float degree = message.getInt("msg");
                GimbalUtil.adjustGimbalPitch(degree);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMissionStateRcv(JSONObject message) {
            try {
                if (waypointMissionBuilder.build() != null) {
                    switch (message.getString("msg")) {
                        case "pause":
                            getWaypointMissionOperator().pauseMission(djiError -> Log.d(TAG, "onMissionStateRcv: " + djiError.getDescription()));
                            break;
                        case "resume":
                            getWaypointMissionOperator().resumeMission(djiError -> Log.d(TAG, "onMissionStateRcv: " + djiError.getDescription()));
                            break;
                        default:
                            break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onVirtualStickDataRcv(JSONObject message) {
            mvirtualSticksController.virtualStickDataRcv(message);
        }

        @Override
        public void onVirtualStickSwitchRcv(JSONObject message) {
            try {
                if (mvirtualSticksController != null) {
                    switch (message.getString("msg")) {
                        case "on":
                            mvirtualSticksController.enableVirtualSticks();
                            break;
                        case "off":
                            mvirtualSticksController.disableVirtualSticks();
                            break;
                        default:
                            break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMECDataRcv(JSONObject message) {
            try {
                int type = (int) message.get("type");
                long servertime = message.getLong("time");
                long delay = System.currentTimeMillis() - servertime;
                Log.d(TAG, "onMECDataRcv: delay" + delay + "servertime: " + servertime + "currtime: " + System.currentTimeMillis());
                switch (type) {
                    case 1:
                        logcatDxDyOnUI("null", "null", "null", "null", "null", delay, "null");
                        //社交距离检测识别框绘制
                        visAIResult(message, type);
                        mAISurfaceViewManager.draw(message);
                        break;
                    case 2:
                        //二次定位识别框绘制
                        visAIResult(message, type);
                        mAISurfaceViewManager.drawCV(message);
                        //把yolox检测结果传给虚拟摇杆
                        Gson gson = new Gson();
                        VirtualStickData virtualStickData = gson.fromJson(message.getString("msg"), VirtualStickData.class);
//                        显示真实距离估算值（单位：毫米）以及延迟（单位：毫秒）以及像素距离（单位：px像素点）到UI
                        logcatDxDyOnUI(
                                String.valueOf(virtualStickData.getDx()),
                                String.valueOf(virtualStickData.getDy()),
                                String.valueOf(virtualStickData.getMroll()),
                                String.valueOf(virtualStickData.getMpitch()),
                                virtualStickData.getBoxAreas(),
                                delay,
                                virtualStickData.getStrDirection()  //备选停机坪方向
                        );
                        mvirtualSticksController.decodeControllData(message.getString("msg"), curr_hight);
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMultLive(JSONObject message) {
            mSignalClient.leaveRoom();
            try {
                String newRoomId = message.getString("roomId");
                mMultLiveId = newRoomId;
                mSignalClient.joinRoom(mUserID, mMultLiveId);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onMultLiveLeave(JSONObject message) {
            mSignalClient.leaveRoom();
            mSignalClient.joinRoom(mUserID, mRoomId);
        }
    };

    public void visAIResult(JSONObject message, int detectType) throws JSONException {
        if (recordJSONObject != null) {
            switch (detectType) {
                case 1:
                    //社交距离检测识别框绘制
                    recordJSONObject.put("rect_list", message.get("rect_list"));
                    recordJSONObject.put("line_list", message.get("line_list"));
                    break;
                case 2:
                    //二次定位识别框绘制
                    recordJSONObject.put("rect_list", message.get("CVrect_list"));
                    recordJSONObject.put("line_list", message.get("CVline_list"));
                    break;
                default:
                    break;
            }
            recordJSONObject.put("warning", message.get("warning"));
            RequestParams params = new RequestParams("http://" + localIp + ":8211/record/update");
            params.setConnectTimeout(10 * 1000);
            params.setReadTimeout(10 * 1000);
            params.setMultipart(true);
            params.addHeader("head", "android");
            params.addHeader("token", recordToken);
            params.addParameter("deviation", message.get("deviation"));
            params.addParameter("recordId", recordId);
            params.addParameter("recordDetail", recordJSONObject.toString());
            Log.i(TAG, "onMessage params BODY: " + params.getBodyContent());
            x.Ext.init(getApplication());
            x.Ext.setDebug(BuildConfig.DEBUG);
            x.http().post(params, new Callback.CommonCallback<String>() {
                @Override
                public void onSuccess(final String result) {
                    Log.i(TAG, "on record/update Success: " + result);
                }

                @Override
                public void onError(Throwable ex, boolean isOnCallback) {
                    Log.i(TAG, "on record/update Error: " + ex.getMessage());
                }

                @Override
                public void onCancelled(CancelledException cex) {
                }

                @Override
                public void onFinished() {
                }
            });
        }
    }

    private void logcatOnUI(String msg) {
        Log.i(TAG, msg);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String output = mLogcatView.getText() + "\n" + msg;
//                String output =  msg;

                mLogcatView.setText(output);
            }
        });
    }

    /**
     * 更新真实距离估算值以及像素距离到UI
     *
     * @param dx     横向真实距离估算值
     * @param dy     纵向真实距离估算值
     * @param mRoll  横向像素距离
     * @param mPitch 纵向像素距离
     * @param delay  算法识别时间花销
     */
    private void logcatDxDyOnUI(String dx, String dy, String mRoll, String mPitch, String boxAreas, long delay, String urgencyDirection) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder
                        .append("\n")
                        .append("真实距离Dx: " + dx + " mm\n")
                        .append("真实距离Dy: " + dy + " mm\n")
                        .append("像素距离Dx: " + mRoll + " px\n")
                        .append("像素距离Dy: " + mPitch + " px\n")
                        .append("Box面积: " + boxAreas + "\n")
                        .append("算法识别时延: " + delay + " ms\n")
                        .append("备选停机坪位置: " + urgencyDirection);
                mLogcatView.setText(stringBuilder);
            }
        });
    }


    private boolean havePLCStatus(PLCStatus... plcStatuses) {
        boolean havePLCStatus = true;
        for (int i = 0; i < plcStatuses.length; i++) {
            if (!plcStatusesList.contains(plcStatuses[i])) {
                havePLCStatus = false;
                break;
            }
        }
        return havePLCStatus;
    }

    private String toJsonSessionDescription(SessionDescription sdp) {
        Log.i(TAG, "toJsonSessionDescription");
        JSONObject json = new JSONObject();

        String type = "offer";
        if (sdp.type == SessionDescription.Type.OFFER) {
            type = "offer";
        } else if (sdp.type == SessionDescription.Type.ANSWER) {
            type = "answer";
        } else if (sdp.type == SessionDescription.Type.PRANSWER) {
            type = "pranswer";
        } else {
            type = "unkown";
            Log.e(TAG, "toJsonSessionDescription failed: unknown the sdp type");
        }
        String sdpDescription = sdp.description;

        try {
            json.put("sdp", sdpDescription);
            json.put("type", type);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        return json.toString();
    }

    private String toJsonCandidate(IceCandidate candidate) {
        JSONObject json = new JSONObject();

        try {
            json.put("id", candidate.sdpMid);
            json.put("label", candidate.sdpMLineIndex);
            json.put("candidate", candidate.sdp);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        return json.toString();
    }

    private IceCandidate toJavaCandidate(JSONObject json) throws JSONException {
        return new IceCandidate(
                json.getString("id"), json.getInt("label"), json.getString("candidate"));
    }

    public void doStartCall() {
        logcatOnUI("Start Call, Wait ...");
        if (mPeerConnection == null) {
            mPeerConnection = createPeerConnection();
        }
        MediaConstraints mediaConstraints = new MediaConstraints();
        mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        mediaConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        mPeerConnection.createOffer(new SimpleSdpObserver() {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                Log.i(TAG, "Create local offer success: \n" + sessionDescription.description);
                mPeerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
                mSignalClient.sendOffer(toJsonSessionDescription(sessionDescription), mRemoteUserId);

            }
        }, mediaConstraints);
    }

    public void doEndCall() {
        if (!mIsJoinRoom) {
            Log.w(TAG, "no join room");
        }
        logcatOnUI("End Call, Wait ...");
        hanup();
        mSignalClient.leaveRoom();
    }

    public static class SimpleSdpObserver implements SdpObserver {
        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            Log.i(TAG, "SdpObserver: onCreateSuccess !");
        }

        @Override
        public void onSetSuccess() {
            Log.i(TAG, "SdpObserver: onSetSuccess");
        }

        @Override
        public void onCreateFailure(String msg) {
            Log.e(TAG, "SdpObserver onCreateFailure: " + msg);
        }

        @Override
        public void onSetFailure(String msg) {
            Log.e(TAG, "SdpObserver onSetFailure: " + msg);
        }
    }


    public void doAnswerCall(String description) {
        logcatOnUI("Answer Call, Wait ...");
        if (mPeerConnection == null) {
            mPeerConnection = createPeerConnection();
        }
        mPeerConnection.setRemoteDescription(new SimpleSdpObserver(), new SessionDescription(SessionDescription.Type.OFFER, description));

        MediaConstraints sdpMediaConstraints = new MediaConstraints();
        MediaConstraints mediaConstraints = new MediaConstraints();
        ArrayList<MediaConstraints.KeyValuePair> keyValuePairs = new ArrayList<>();
        keyValuePairs.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        keyValuePairs.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        mediaConstraints.mandatory.addAll(keyValuePairs);
        Log.i(TAG, "Create answer ...");
        mPeerConnection.createAnswer(new SimpleSdpObserver() {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                Log.i(TAG, "Create answer success !");
                mPeerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
                mSignalClient.sendAnswer(toJsonSessionDescription(sessionDescription), mRemoteUserId);
            }
        }, sdpMediaConstraints);
    }

    private void hanup() {
        logcatOnUI("Hanup Call, Wait ...");
        if (mPeerConnection == null) {
            return;
        }
        mPeerConnection.close();
        mPeerConnection = null;
        logcatOnUI("Hanup Done.");
    }

    public PeerConnection createPeerConnection() {
        Log.i(TAG, "Create PeerConnection ...");
        this.ICEServers = new ArrayList<>();
        if (iceServers != null) {
            for (MyIceServer myIceServer : iceServers) {
                PeerConnection.IceServer iceServer = PeerConnection.IceServer
                        .builder(myIceServer.uri)
                        .setUsername(myIceServer.username)
                        .setPassword(myIceServer.password)
                        .createIceServer();
                ICEServers.add(iceServer);
            }
        }

        PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(ICEServers); // 传入coturn的stun和turn的地址
        configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;   // 设置为中继模式
        PeerConnection connection = mPeerConnectionFactory.createPeerConnection(configuration, mPeerConnectionObserver);
        if (connection == null) {
            Log.e(TAG, "Failed to createPeerConnection !");
            return null;
        }
//        connection.addStream(mLocalStream);
        List<String> mediaStreamLabels = Collections.singletonList("ARDAMS");
        connection.addTrack(mVideoTrack, mediaStreamLabels);
        connection.addTrack(mAudioTrack, mediaStreamLabels);
//        connection.addTrack(mVideoTrack);
//        connection.addTrack(mAudioTrack);
        return connection;
    }

    public void onClickStartCallButton() {
        Log.i(TAG, "onClickStartCallButton");
        mSignalClient.joinRoom(mUserID, mRoomId);
        mIsJoinRoom = true;
    }

    public void onClickEndCallButton() {
        Log.i(TAG, "onClickEndCallButton");
        doEndCall();
        mIsJoinRoom = false;
    }

    private void onViewClick(View view) {
        if (view == videostreamPreviewSf && !isMapMini) {
            //mapwidget->mini
            show_panel();
            resizeFPVWidget(
                    RelativeLayout.LayoutParams.MATCH_PARENT,
                    RelativeLayout.LayoutParams.MATCH_PARENT,
                    0,
                    0);
            reorderCameraCapturePanel();
            ResizeAnimation mapViewAnimation = new ResizeAnimation(
                    mapWidget,
                    RelativeLayout.LayoutParams.MATCH_PARENT,
                    RelativeLayout.LayoutParams.MATCH_PARENT,
                    width, height,
                    0);
            mapWidget.startAnimation(mapViewAnimation);
            isMapMini = true;
        } else if (view == mapWidget && isMapMini) {
            //mapwidget->max
            hidePanels();
            resizeFPVWidget(
                    width, height,
                    0, 12);
            reorderCameraCapturePanel();
            ResizeAnimation mapViewAnimation = new ResizeAnimation(mapWidget,
                    width, height,
                    RelativeLayout.LayoutParams.MATCH_PARENT,
                    RelativeLayout.LayoutParams.MATCH_PARENT, 0);
            mapWidget.startAnimation(mapViewAnimation);
            isMapMini = false;
        }
    }

    private void resizeFPVWidget(int width, int height, int margin, int fpvInsertPosition) {
        RelativeLayout.LayoutParams fpvParams = (RelativeLayout.LayoutParams) videostreamPreviewSf.getLayoutParams();
        fpvParams.height = height;
        fpvParams.width = width;
        fpvParams.rightMargin = margin;
        fpvParams.bottomMargin = margin;
        if (isMapMini) {
            fpvParams.addRule(RelativeLayout.CENTER_IN_PARENT, 0);
            fpvParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
            fpvParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
        } else {
            fpvParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0);
            fpvParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0);
            fpvParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
        }
        videostreamPreviewSf.setLayoutParams(fpvParams);
        parentView.removeView(videostreamPreviewSf);
        parentView.addView(videostreamPreviewSf, fpvInsertPosition);
    }

    private void reorderCameraCapturePanel() {
        View cameraCapturePanel = findViewById(R.id.CameraCapturePanel);
        parentView.removeView(cameraCapturePanel);
        parentView.addView(cameraCapturePanel, isMapMini ? 9 : 13);
    }

    private void hidePanels() {
        //These panels appear based on keys from the drone itself.
        if (KeyManager.getInstance() != null) {
            KeyManager.getInstance().setValue(CameraKey.create(CameraKey.HISTOGRAM_ENABLED), false, null);
            KeyManager.getInstance().setValue(CameraKey.create(CameraKey.COLOR_WAVEFORM_ENABLED), false, null);
        }

        //These panels have buttons that toggle them, so call the methods to make sure the button state is correct.
        CameraControlsWidget controlsWidget = findViewById(R.id.CameraCapturePanel);
        controlsWidget.setAdvancedPanelVisibility(false);
        controlsWidget.setExposurePanelVisibility(false);

        //These panels don't have a button state, so we can just hide them.
        findViewById(R.id.pre_flight_check_list).setVisibility(View.GONE);
        findViewById(R.id.rtk_panel).setVisibility(View.GONE);
        findViewById(R.id.spotlight_panel).setVisibility(View.GONE);
        findViewById(R.id.speaker_panel).setVisibility(View.GONE);

        findViewById(R.id.function_panel).setVisibility(View.GONE);
        findViewById(R.id.button_list).setVisibility(View.GONE);
        findViewById(R.id.b_point_panel).setVisibility(View.VISIBLE);
    }

    private void show_panel() {
        findViewById(R.id.function_panel).setVisibility(View.VISIBLE);
        findViewById(R.id.button_list).setVisibility(View.VISIBLE);
        findViewById(R.id.b_point_panel).setVisibility(View.GONE);
    }

    private void initSurfaceOrTextureView() {
        /**
         * we also need init the textureView because the pre-transcoded video steam will display in the textureView
         */
        initPreviewerTextureView();
        /**
         * we use standardVideoFeeder to pass the transcoded video data to DJIVideoStreamDecoder, and then display it
         * on surfaceView
         */
        initPreviewerSurfaceView();
    }

    /**
     * 隐藏虚拟按键
     */
    public void hideNavigationBar() {
        int uiFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                | View.SYSTEM_UI_FLAG_FULLSCREEN; // hide status bar
        if (Build.VERSION.SDK_INT >= 19) {
            uiFlags |= View.SYSTEM_UI_FLAG_IMMERSIVE;//0x00001000; // SYSTEM_UI_FLAG_IMMERSIVE_STICKY: hide
        } else {
            uiFlags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
        }
        try {
            getWindow().getDecorView().setSystemUiVisibility(uiFlags);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * Init a fake texture view to for the codec manager, so that the video raw data can be received
     * by the camera
     */
    private void initPreviewerTextureView() {
        videostreamPreviewTtView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                hideNavigationBar();
            }
        });
        videostreamPreviewTtView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                Log.d(TAG, "real onSurfaceTextureAvailable");
                videoViewWidth = width;
                videoViewHeight = height;
                Log.d(TAG, "real onSurfaceTextureAvailable: width " + videoViewWidth + " height " + videoViewHeight);
                if (mCodecManager == null) {
                    mCodecManager = new DJICodecManager(getApplicationContext(), surface, width, height);
                    //For M300RTK, you need to actively request an I frame.
                    mCodecManager.resetKeyFrame();
                }
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
                videoViewWidth = width;
                videoViewHeight = height;
                Log.d(TAG, "real onSurfaceTextureAvailable2: width " + videoViewWidth + " height " + videoViewHeight);
                mCodecManager.onSurfaceSizeChanged(width, height, 0);
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                if (mCodecManager != null) {
                    mCodecManager.cleanSurface();
                    mCodecManager.destroyCodec();
                    mCodecManager = null;
                }
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

    /**
     * Init a surface view for the DJIVideoStreamDecoder
     */
    private void initPreviewerSurfaceView() {
        videostreamPreviewSh = videostreamPreviewSf.getHolder();
        surfaceCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                Log.d(TAG, "real onSurfaceTextureAvailable");
                videoViewWidth = videostreamPreviewSf.getWidth();
                videoViewHeight = videostreamPreviewSf.getHeight();
                Log.d(TAG, "real onSurfaceTextureAvailable3: width " + videoViewWidth + " height " + videoViewHeight);

                // USE_SURFACE_VIEW_DEMO_DECODER ,This demo might not work well on P3C and OSMO.
                NativeHelper.getInstance().init();
                DJIVideoStreamDecoder.getInstance().init(getApplicationContext(), holder.getSurface());
                DJIVideoStreamDecoder.getInstance().resume();

            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                videoViewWidth = width;
                videoViewHeight = height;
                DJIVideoStreamDecoder.getInstance().changeSurface(holder.getSurface());

                Log.d(TAG, "real onSurfaceTextureAvailable4: width " + videoViewWidth + " height " + videoViewHeight);

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                DJIVideoStreamDecoder.getInstance().stop();
                NativeHelper.getInstance().release();

            }
        };

        videostreamPreviewSh.addCallback(surfaceCallback);
    }

    public void registerSDK() {
        DJISDKManager.getInstance().registerApp(CompleteWidgetActivity.this, new DJISDKManager.SDKManagerCallback() {
            @Override
            public void onRegister(DJIError djiError) {
                if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
                    DJILog.e("App registration", DJISDKError.REGISTRATION_SUCCESS.getDescription());
                    DJISDKManager.getInstance().startConnectionToProduct();
                    //notifyStatusChange();
                }
                Log.v(TAG, djiError.getDescription());
            }

            @Override
            public void onProductDisconnect() {
                Log.e(TAG, "onProductDisconnect");
                //notifyStatusChange();
            }

            @Override
            public void onProductConnect(BaseProduct baseProduct) {
                Log.e(TAG, String.format("onProductConnect newProduct:%s", baseProduct));
                //notifyStatusChange();
            }

            @Override
            public void onProductChanged(BaseProduct baseProduct) {
                Log.e(TAG, String.format("onProductChanged newProduct:%s", baseProduct.toString()));
                if (baseProduct.toString().equals("None")) {
                    Log.e(TAG, String.format("onProductChanged status: %s", "disconnect"));
                    MediaUploadUtil.updateStatus(mRoomId, 1);
                } else {
                    Log.e(TAG, String.format("onProductChanged status: %s", "connected"));
                    notifyStatusChange();
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if (mFlightController != null) initFlightController();
                        }
                    }, 1000);
                }
            }

            @Override
            public void onComponentChange(BaseProduct.ComponentKey componentKey,
                                          BaseComponent oldComponent,
                                          BaseComponent newComponent) {
                Log.e(TAG, String.format("onComponentChange key:%s, oldComponent:%s, newComponent:%s",
                        componentKey,
                        oldComponent,
                        newComponent));
                //notifyStatusChange();
            }

            @Override
            public void onInitProcess(DJISDKInitEvent djisdkInitEvent, int i) {
            }

            @Override
            public void onDatabaseDownloadProgress(long current, long total) {
            }
        });

    }

    private long lastupdate;

    private void notifyStatusChange() {

        final BaseProduct product = DJISampleApplication.getProductInstance();
        Log.d(TAG, "notifyStatusChange: " + (product == null ? "Disconnect" : (product.getModel() == null ? "null model" : product.getModel().name())));
        if (null == product || !product.isConnected()) {
            mCamera = null;
            showToast("Disconnected");
        } else {
            if (!product.getModel().equals(Model.UNKNOWN_AIRCRAFT)) {
                showToast(product.toString() + " connected");
                mCamera = product.getCamera();

                if (DJISampleApplication.getCameraInstance() != null) {

                    mCamera.setSystemStateCallback(systemState -> {
                        Log.d("Media", "CurrentPanoramaPhotoCount: " + systemState.getCurrentPanoramaPhotoCount());
                        Log.d("Media", "PanoramaProgress: " + systemState.getPanoramaProgress());
                        int pan_count = systemState.getCurrentPanoramaPhotoCount();
                        int pan_progress = systemState.getPanoramaProgress();
                        runOnUiThread(() -> mPanoTaskPanel.updatePanoProgress(shootcount, pan_progress, pan_count <= 0 ? 0 : pan_count));
                        if (pan_count == -1 && pan_progress == 100) {
                            panActionStatus = PanActionStatus.FINISH_ACTION;
                            runOnUiThread(() -> mPanoTaskPanel.updatePanoStatus(shootcount, "完成拍摄"));
                        } else if (pan_count > 0) {
                            panActionStatus = PanActionStatus.EXECUTING;
                            runOnUiThread(() -> mPanoTaskPanel.updatePanoStatus(shootcount, "正在拍摄"));
                        } else if (pan_progress <= 0) {
                            panActionStatus = PanActionStatus.NOT_EXECUTING;
                            runOnUiThread(() -> mPanoTaskPanel.updatePanoStatus(shootcount, "准备拍摄"));
                        }
                        Log.d("Media", "panActionStatus: " + panActionStatus);
                    });


                    mCamera.setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO, djiError -> {
                        if (djiError != null)
                            Log.d("wwww", "setMode: " + djiError.toString());
                    });
                    mCamera.setShootPhotoMode(SettingsDefinitions.ShootPhotoMode.PANORAMA, djiError -> {
                        if (djiError != null)
                            Log.d("wwww", "setShootPhotoMode: " + djiError.toString());
                    });
                    mCamera.setPhotoPanoramaMode(SettingsDefinitions.PhotoPanoramaMode.PANORAMA_MODE_SPHERE, djiError -> {
                        if (djiError != null)
                            Log.d("wwww", "setPhotoPanoramaMode: " + djiError.toString());
                    });
                    mCamera.setPanoOriginalPhotoSettings(new OriginalPhotoSettings(true, SettingsDefinitions.PhotoFileFormat.RAW), djiError -> {
                        if (djiError != null)
                            Log.d("wwww", "setPanoOriginalPhotoSettings: " + djiError.toString());
                    });
                    mCamera.setFocusMode(SettingsDefinitions.FocusMode.AFC, djiError -> {
                        if (djiError != null)
                            Log.d("wwww", "setFocusMode: " + djiError.toString());
                    });

                    mReceivedVideoDataListener = (videoBuffer, size) -> {
                        if (System.currentTimeMillis() - lastupdate > 1000) {
                            Log.d(TAG, "camera recv video data size: " + size);
                            lastupdate = System.currentTimeMillis();
                        }
                        /**
                         we use standardVideoFeeder to pass the transcoded video data to DJIVideoStreamDecoder, and then display it
                         * on surfaceView
                         */
                        DJIVideoStreamDecoder.getInstance().parse(videoBuffer, size);
                    };
                    if (VideoFeeder.getInstance() != null) {

                        //When calibration is needed or the fetch key frame is required by SDK, should use the provideTranscodedVideoFeed
                        //to receive the transcoded video feed from main camera.
                        standardVideoFeeder = VideoFeeder.getInstance().provideTranscodedVideoFeed();
                        if (standardVideoFeeder != null && this.mReceivedVideoDataListener != null
                                && standardVideoFeeder.getListeners().contains(mReceivedVideoDataListener)) {
                            standardVideoFeeder.removeVideoDataListener(mReceivedVideoDataListener);
                        }
                        standardVideoFeeder.addVideoDataListener(mReceivedVideoDataListener);
                    }
                }
            }
        }
    }

    /**
     * 设置控件隐藏/可见
     */
    private void openOrCloseView(View view) {
        if (view.getVisibility() != View.VISIBLE) {
            view.setVisibility(View.VISIBLE);
        } else {
            view.setVisibility(View.GONE);
        }
    }

    void startLiveShow(String recordName) {
        Log.i(TAG, "startLiveShow");
        if (recordName.equals("")) {
            rtmp_url = rtmp_server + "test";
        } else {
            rtmp_url = rtmp_server + recordName;
        }
        ToastUtils.showShort(CompleteWidgetActivity.this, "Start Live Show");
        if (!isLiveStreamManagerOn()) {
            return;
        }
        if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
            ToastUtils.showShort(CompleteWidgetActivity.this, "already started!");
            return;
        }
        try {
            RequestParams params = new RequestParams("http://" + localIp + ":8211/record/create");
            params.setConnectTimeout(10 * 1000);
            params.setReadTimeout(10 * 1000);
            params.setMultipart(true);
            params.addHeader("head", "android");
            params.addHeader("token", recordToken);
            params.addParameter("recordName", recordName);
            params.addParameter("recordUrl", "http://" + localIp + "/record/" + recordName + ".mp4");
            x.Ext.init(getApplication());
            x.Ext.setDebug(BuildConfig.DEBUG);
            x.http().post(params, new Callback.CommonCallback<String>() {
                @Override
                public void onSuccess(final String result) {
                    Log.i(TAG, "on record/create Success: " + result);
                    try {
                        JSONObject jsonObject = new JSONObject(result);
                        JSONObject data = new JSONObject(jsonObject.getString("data"));
                        recordId = data.getInt("recordId");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onError(Throwable ex, boolean isOnCallback) {
                    Log.i(TAG, "on record/create Error: " + ex.getMessage());
                }

                @Override
                public void onCancelled(CancelledException cex) {
                }

                @Override
                public void onFinished() {
                }
            });
            ExcelDataUtil.creatNewBook(recordName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread() {
            @Override
            public void run() {
                DJISDKManager.getInstance().getLiveStreamManager().setAudioMuted(true);
                DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(true);
                DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoBitRateMode(LiveVideoBitRateMode.AUTO);
//                DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoBitRate(600f);
                DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoResolution(LiveVideoResolution.VIDEO_RESOLUTION_1920_1080);
                DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl(rtmp_url);
                int result = DJISDKManager.getInstance().getLiveStreamManager().startStream();
                ToastUtils.showShort(CompleteWidgetActivity.this, rtmp_url);
                ToastUtils.showShort(CompleteWidgetActivity.this, "startLive:" + result +
                        "\n isVideoStreamSpeedConfigurable:" + DJISDKManager.getInstance().getLiveStreamManager().isVideoStreamSpeedConfigurable() +
                        "\n isLiveAudioEnabled:" + DJISDKManager.getInstance().getLiveStreamManager().isLiveAudioEnabled());
                //发送信令通知算法服务器拉流
                try {
                    sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mSignalClient.sendMECSignal(mUserID, mRoomId, recordName,
                        murgencyLandController.urlandSwitch,
                        murgencyLandController.urlandPosition);
                if (murgencyLandController.urlandSwitch == 0)
                    VirtualSticksController.readyAlternating = false;
                else if (murgencyLandController.urlandSwitch == 1)
                    VirtualSticksController.readyAlternating = true;

            }
        }.start();
    }

    private void stopLiveShow() {
        if (!isLiveStreamManagerOn()) {
            return;
        }
        DJISDKManager.getInstance().getLiveStreamManager().stopStream();
        ToastUtils.showShort(CompleteWidgetActivity.this, "Stop Live Show");
    }


    private boolean isLiveStreamManagerOn() {
        if (DJISDKManager.getInstance().getLiveStreamManager() == null) {
            ToastUtils.showShort(CompleteWidgetActivity.this, "No live stream manager!");
            return false;
        }
        return true;
    }

    public void set_HomePoint_dialog() {
        LayoutInflater inflater = LayoutInflater.from(CompleteWidgetActivity.this);
        View vv = inflater.inflate(R.layout.make_gps_dialog, null);
        final EditText lat = vv.findViewById(R.id.gps_text_lat);
        final EditText lng = vv.findViewById(R.id.gps_text_lng);
        if (curMarker != null) {
            lat.setText(String.valueOf(curMarker.latitude));
            lng.setText(String.valueOf(curMarker.longitude));

        }
        new AlertDialog.Builder(CompleteWidgetActivity.this, R.style.jzxz_dialog)
                .setView(vv)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mFlightController.setHomeLocation(new LocationCoordinate2D(Double.parseDouble(String.valueOf(lat.getText())), Double.parseDouble(String.valueOf(lng.getText()))), new CommonCallbacks.CompletionCallback() {
                            @Override
                            public void onResult(DJIError djiError) {
                                if (djiError != null) showToast(djiError.toString());
                            }
                        });
                        curMarker.setLatitude(Double.parseDouble(String.valueOf(lat.getText())));
                        curMarker.setLongitude(Double.parseDouble(String.valueOf(lng.getText())));
                        addMarker(Double.parseDouble(String.valueOf(lat.getText())), Double.parseDouble(String.valueOf(lng.getText())));
                        dialog.dismiss();
                    }
                })
                .create()
                .show();
    }


    /**
     * 设置websocket地址
     */
    public void connect_websocket_dialog() {
        LayoutInflater inflater = LayoutInflater.from(CompleteWidgetActivity.this);
        View vv = inflater.inflate(R.layout.connect_websocket_dialog, null);
        final EditText edit_ws_url = vv.findViewById(R.id.websocket_address);
        websocketIP = sharedPreferences.getString(websocket_server, "");
        if (!Objects.equals(websocketIP, "")) edit_ws_url.setText(websocketIP);
        new AlertDialog.Builder(CompleteWidgetActivity.this, R.style.jzxz_dialog)
                .setView(vv)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String url = edit_ws_url.getText().toString();
                        websocketIP = url;
                        /*实例化SharedPreferences.Editor对象*/
                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.putString(websocket_server, url);
                        editor.apply();

                        if (!TextUtils.isEmpty(url)) {
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    WebSocketManager.getInstance().init(url, CompleteWidgetActivity.this);
                                }
                            }).start();
                        } else {
                            Toast.makeText(getBaseContext(), "请填写需要链接的地址", Toast.LENGTH_SHORT).show();
                        }
                        dialog.dismiss();
                    }
                })
                .create()
                .show();
    }

    /**
     * 设置RTMP推流地址
     */
    public void SetLiveStreamAdd_dialog() {
        LayoutInflater inflater = LayoutInflater.from(CompleteWidgetActivity.this);
        View vv = inflater.inflate(R.layout.set_livestream_add_dialog, null);
        final EditText edit_rtmp_address = vv.findViewById(R.id.rtmp_address);
//        rtmp_server = sharedPreferences.getString(rtmp_address,"");
//        if (!Objects.equals(rtmp_server, "")) edit_rtmp_address.setText(rtmp_server);
        new AlertDialog.Builder(CompleteWidgetActivity.this, R.style.jzxz_dialog)
                .setView(vv)
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String rtmp_ip = edit_rtmp_address.getText().toString();
//                        SharedPreferences.Editor editor = sharedPreferences.edit();
//                        editor.putString(rtmp_address,rtmp_ip);
//                        editor.apply();

                        if (!TextUtils.isEmpty(rtmp_ip)) {
//                            rtmp_server = rtmp_ip;
                            recordName = rtmp_ip;
                            SimpleUtils.showToast(CompleteWidgetActivity.this, "录播名称设置为： " + recordName);
                        } else {
                            Toast.makeText(getBaseContext(), "请填写需要链接的地址", Toast.LENGTH_SHORT).show();
                        }
                        dialog.dismiss();
                    }
                })
                .create()
                .show();
    }


    private void initFlightController() {
        mFlightController = null;
        BaseProduct product = DJISampleApplication.getProductInstance();
        if (product != null && product.isConnected()) {
            if (product instanceof Aircraft) {
                mFlightController = ((Aircraft) product).getFlightController();
                mBatteryInstance = product.getBattery();
                product.getName(new CommonCallbacks.CompletionCallbackWith<String>() {
                    @Override
                    public void onSuccess(String s) {
                        product_name = s;
                        Log.i(TAG, "getProductName onSuccess: " + product_name);
                        if (mFlightController != null) {
                            mvirtualSticksController = new VirtualSticksController(CompleteWidgetActivity.this);
                            //设置虚拟摇杆控制模式
                            mFlightController.setRollPitchControlMode(RollPitchControlMode.VELOCITY);
                            if (VirtualSticksController.readyAlternating) {
                                mFlightController.setYawControlMode(YawControlMode.ANGLE);
                            } else {
                                mFlightController.setYawControlMode(YawControlMode.ANGULAR_VELOCITY);
                            }
                            mFlightController.setVerticalControlMode(VerticalControlMode.VELOCITY);
                            mFlightController.setRollPitchCoordinateSystem(FlightCoordinateSystem.BODY);
                            mFlightController.getSerialNumber(new CommonCallbacks.CompletionCallbackWith<String>() {
                                @Override
                                public void onSuccess(String s) {
                                    Log.d(TAG, "getSerialNumber onSuccess: " + s);
                                    mRoomId = s;
                                    MediaUploadUtil.updateStatus(s, 2);
                                }

                                @Override
                                public void onFailure(DJIError djiError) {

                                }
                            });


                            mFlightController.setStateCallback(flightControllerState -> {
                                if (flightControllerState.isLandingConfirmationNeeded()) {
                                    mFlightController.confirmLanding(new CommonCallbacks.CompletionCallback() {
                                        @Override
                                        public void onResult(DJIError djiError) {
                                            if (djiError != null)
                                                showToast(djiError.getDescription());
                                        }
                                    });
                                }
                                if (isMissionFinish && !flightControllerState.areMotorsOn()) {
                                    isMissionFinish = false;
                                    isLanding = false;
                                    stopLiveShow();
                                    new Thread(() -> {
//                                            try {
//                                                tcpDrone.sendCommand(PLCCommand.after_landing);
//                                                sleep(5000);
//                                                tcpDrone.sendCommand(PLCCommand.change_battery);
//                                            } catch (InterruptedException e) {
//                                                e.printStackTrace();
//                                            }
                                        disableVirtualStick();
                                        stopLiveShow();
                                    }).start();
                                }
                                StringBuilder sb = new StringBuilder();
                                droneLocationLat = flightControllerState.getAircraftLocation().getLatitude();
                                curr_hight = flightControllerState.getAircraftLocation().getAltitude();
                                if (flightControllerState.getAircraftLocation().getAltitude() < 10f && flightControllerState.isUltrasonicBeingUsed()) {
//                                    Log.d(TAG, "on Update curr hight: isUltrasonicBeingUsed");
                                    curr_hight = flightControllerState.getUltrasonicHeightInMeters();
//                                    Log.d(TAG, "on Update curr hight: " + curr_hight);
                                } else {
//                                    Log.d(TAG, "on Update curr hight: UltrasonicNotUsed");
                                }

                                //完成任务后自动返航，监听无人机降落高度，小于10米时切换到视觉定位进行降落
                                if (flightControllerState.getGoHomeExecutionState() != null) {
//                                    Log.d(TAG, "getGoHomeExecutionState: " + flightControllerState.getGoHomeExecutionState().toString());
                                    if (!LandInspect) {
                                        if (isLanding && isMissionFinish && curr_hight <= 10f &&
                                                flightControllerState.getGoHomeExecutionState() == GoHomeExecutionState.GO_DOWN_TO_GROUND) {
                                            mFlightController.cancelGoHome(new CommonCallbacks.CompletionCallback() {
                                                @Override
                                                public void onResult(DJIError djiError) {
                                                    if (djiError != null) {
                                                        logcatOnUI(djiError.getDescription());
                                                    } else {
//                                                        startLiveShow(recordName + "-landing");
//                                                        enableVirtualStick();
                                                    }
                                                }
                                            });
                                        }
                                        //备降
                                        if (VirtualSticksController.readyAlternating && curr_hight <= 10f &&
                                                flightControllerState.getGoHomeExecutionState() == GoHomeExecutionState.GO_DOWN_TO_GROUND) {
                                            mFlightController.cancelGoHome(new CommonCallbacks.CompletionCallback() {
                                                @Override
                                                public void onResult(DJIError djiError) {
                                                    if (djiError != null) {
                                                        logcatOnUI(djiError.getDescription());
                                                    } else {
                                                        enableVirtualStick();
                                                    }
                                                }
                                            });
                                        }
                                    }
                                }
                                droneLocationLng = flightControllerState.getAircraftLocation().getLongitude();
//                    Log.i("LAT------LNG", String.valueOf(droneLocationLat) + "____" + droneLocationLng);
                                current_latlng = new DJILatLng(droneLocationLat, droneLocationLng, curr_hight);
                                curr_amapLatlng = GPSUtil.DJILatLng_To_gps84_To_Gcj02_ToLatlng(current_latlng);
                                //获取当前无人机2D坐标
                                sb.append("cur_LatLng:").append("\n")
                                        .append(flightControllerState.getAircraftLocation().getLatitude()).append("\n")
                                        .append(flightControllerState.getAircraftLocation().getLongitude()).append("\n");
                                sb.append("height:").append(flightControllerState.getAircraftLocation().getAltitude()).append("\n");
                                if (flightControllerState.getHomeLocation() != null) {
                                    homepoint = new DJILatLng(
                                            flightControllerState.getHomeLocation().getLatitude(),
                                            flightControllerState.getHomeLocation().getLongitude());

                                    final float distance = AMapUtils.calculateLineDistance(
                                            GPSUtil.DJILatLng_To_gps84_To_Gcj02_ToLatlng(homepoint),
                                            GPSUtil.DJILatLng_To_gps84_To_Gcj02_ToLatlng(current_latlng));

                                    sb.append("homepoint:").append("\n")
                                            .append(homepoint.latitude).append("\n")
                                            .append(homepoint.longitude).append("\n");
                                    sb.append("distance:").append(distance);
                                }
                                if (mFlightController != null)
                                    fxj_heading_cur = mFlightController.getCompass().getHeading();
//                    Log.i("head_direction:", String.valueOf(flightControllerState.getAircraftHeadDirection()));
//                    Log.i("compass_direction:", String.valueOf(fxj_heading_cur));
                                int angle = (int) fxj_heading_cur;
                                if (angle < 0)
                                    fxj2 = angle + 360;
                                else
                                    fxj2 = angle;
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        GPS_panel.setText(sb.toString());
                                        updateStateView();
                                    }
                                });
                                double xspeed = flightControllerState.getVelocityX();
                                double yspeed = flightControllerState.getVelocityY();
                                double zspeed = flightControllerState.getVelocityZ();
                                try {
                                    JSONObject args = new JSONObject();
                                    args.put("sn", mRoomId);
                                    args.put("product", product_name);
                                    args.put("altitude", flightdataFormat.format(curr_hight));
                                    args.put("batteryRemainInPercent", batteryRemainInPercent);
                                    args.put("hSpeed", flightdataFormat.format(Math.sqrt(xspeed * xspeed + yspeed * yspeed)));
                                    args.put("vspeed", flightdataFormat.format(zspeed));
                                    args.put("heading", fxj2);
                                    args.put("lng", String.valueOf(curr_amapLatlng.longitude));
                                    args.put("lat", String.valueOf(curr_amapLatlng.latitude));
                                    args.put("isMissionReady", isMissionReady);
                                    args.put("isLandReady", isLandReady);
                                    args.put("isTakeOff", isTakeOff);
                                    args.put("waypointMissionState", getWaypointMissionOperator().getCurrentState().toString());
                                    args.put("readyTakeOffOK", havePLCStatus(PLCStatus.HAVE_DRONE, PLCStatus.READY_TAKEOFF_OK));
                                    if (weatherParams != null) {
                                        args.put("weatherParams", JSON.toJSONString(weatherParams));
                                    }
//                                        args.put("weatherParams", JSON.toJSONString(new WeatherParams(1,2,3,4,5,6,7,8,9,10,11,12,13,14)));
                                    recordJSONObject = new JSONObject();
                                    recordJSONObject.put("altitude", flightdataFormat.format(curr_hight));
                                    recordJSONObject.put("lng", String.valueOf(curr_amapLatlng.longitude));
                                    recordJSONObject.put("lat", String.valueOf(curr_amapLatlng.latitude));
                                    if (mRemoteUserId != null) {
                                        mSignalClient.sendFlightdata(args.toString(), mRemoteUserId);
                                    }
                                } catch (JSONException e) {
                                    e.printStackTrace();
                                }
                            });
                        }

                    }

                    @Override
                    public void onFailure(DJIError djiError) {
                    }
                });
//                showToast("flightcontroller open");
            }
        }
        if (mBatteryInstance != null) {
            mBatteryInstance.setStateCallback(new BatteryState.Callback() {
                @Override
                public void onUpdate(BatteryState batteryState) {
                    batteryRemainInPercent = batteryState.getChargeRemainingInPercent();
                }
            });
        }
    }

    /**
     * 更新界面经纬度数值
     */
    @SuppressLint("SetTextI18n")
    private void updateStateView() {
        tv_longitude.setText("经度：" + curr_amapLatlng.longitude);
        tv_latitude.setText("纬度：" + curr_amapLatlng.latitude);
        tv_directionAngle.setText("方向角：" + fxj2);
    }

    public void initUI(Bundle savedInstanceState) {


        mLogcatView = findViewById(R.id.LogcatView);

        mPanoTaskPanel = findViewById(R.id.pano_panel);
        show_pano = findViewById(R.id.show_pano_progress);

        mAISurfaceView = findViewById(R.id.AISurfaceView);
        mAISurfaceView.setZOrderOnTop(true);
        mAISurfaceView.setClickable(false);
        mAISurfaceHolder = mAISurfaceView.getHolder();
        mAISurfaceHolder.setFormat(PixelFormat.TRANSPARENT);

        mAISurfaceViewManager = new AISurfaceViewManager(CompleteWidgetActivity.this);
        mAISurfaceViewManager.setAISurfaceViewHolder(mAISurfaceHolder);

        addwaypoint_btn = findViewById(R.id.addwaypoint);
        start_waypoint_mission = findViewById(R.id.start_waypoint_mission);
        B_lat = findViewById(R.id.B_lat);
        B_lng = findViewById(R.id.B_lng);


        tv_longitude = findViewById(R.id.tv_longitude);
        tv_latitude = findViewById(R.id.tv_latitude);
        tv_directionAngle = findViewById(R.id.tv_directionAngle);

        GPS_panel = findViewById(R.id.GPS_panel);

        show_GPS = findViewById(R.id.show_GPS);
        show_GPS.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (GPS_panel.getVisibility() != View.VISIBLE) {
                    /*
                    try {
                        JSONObject args = new JSONObject();
                        args.put("cmd","ABTest");
                        args.put("APointLat", 22.560223097165984);
                        args.put("APointLng", 113.11157242208719);
                        args.put("BPointLat", 22.56018718127374);
                        args.put("BPointLng", 113.11141819506884);
                        args.put("ACTLat", 22.56015718127374);
                        args.put("ACTLng", 113.11143819506884);
                        float distance = AMapUtils.calculateLineDistance(
                                GPSUtil.gps84_To_Gcj02_ToLatlng(22.56018718127374,113.11141819506884),
                                GPSUtil.gps84_To_Gcj02_ToLatlng(22.56015718127374, 113.11143819506884));
                        args.put("distance", distance);
                        WebSocketManager.getInstance().sendMessage(args.toString());
                    } catch (JSONException e) {
                        Log.e("onExecutionFinish: ", e.toString());
                        e.printStackTrace();
                    }*/

                    GPS_panel.setVisibility(View.VISIBLE);
                } else {
                    GPS_panel.setVisibility(View.GONE);
                }
            }
        });
        set_homepoint = findViewById(R.id.set_home);
        set_homepoint.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                set_HomePoint_dialog();
            }
        });
        btn_connect_websocket = findViewById(R.id.btn_websocket);
        btn_connect_websocket.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                connect_websocket_dialog();
            }
        });
        btn_show_funPanel = findViewById(R.id.function_panel);
        btn_show_funPanel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (buttonList.getVisibility() != View.VISIBLE) {
                    buttonList.setVisibility(View.VISIBLE);
                } else {
                    buttonList.setVisibility(View.GONE);
                }
            }
        });
        buttonList = findViewById(R.id.button_list);

        mapWidget = findViewById(R.id.map_widget);
        mapWidget.initAMap(new MapWidget.OnMapReadyListener() {
            @Override
            public void onMapReady(@NonNull DJIMap map) {
                /*
                map.setOnMapClickListener(new DJIMap.OnMapClickListener() {
                    @Override
                    public void onMapClick(DJILatLng latLng) {
                        onViewClick(mapWidget);
                    }
                });
                */
            }
        });

        mapWidget.onCreate(savedInstanceState);
        mapWidget.setFlightPathColor(Color.RED);

        djiMap = mapWidget.getMap();
        djiMap.setOnMapClickListener(new DJIMap.OnMapClickListener() {
            @Override
            public void onMapClick(DJILatLng djiLatLng) {
                onViewClick(mapWidget);
                if (isAdd) {
                    curMarker = djiLatLng;
                    addMarker(djiLatLng.latitude, djiLatLng.longitude);
                }


            }
        });
        if (aMap == null) {
            aMap = (AMap) mapWidget.getMap().getMap();
        }
        MyLocationStyle myLocationStyle;
        //初始化定位蓝点样式类myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点，定位点依照设备方向旋转，并且会跟随设备移动。（1秒1次定位）如果不设置myLocationType，默认也会执行此种模式。
        myLocationStyle = new MyLocationStyle();
        //设置连续定位模式下的定位间隔，只在连续定位模式下生效，单次定位模式下不会生效。单位为毫秒。
        myLocationStyle.interval(10000);
        //定位一次，且将视角移动到地图中心点。
        myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER);
        //连续定位、蓝点不会移动到地图中心点，定位点依照设备方向旋转，并且蓝点会跟随设备移动。
        //myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));
        //myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));
        myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.location_marker));

        //设置定位蓝点的Style
        aMap.setMyLocationStyle(myLocationStyle);
        aMap.showIndoorMap(true);
        aMap.showBuildings(true);
        aMap.setOnMyLocationChangeListener(new AMap.OnMyLocationChangeListener() {
            @Override
            public void onMyLocationChange(Location location) {
                currLocation = location;
            }
        });

        aMap.getUiSettings().setZoomControlsEnabled(false);
        aMap.getUiSettings().setMyLocationButtonEnabled(true);//设置默认定位按钮是否显示，非必需设置。
        aMap.setMyLocationEnabled(true);// 设置为true表示启动显示定位蓝点，false表示隐藏定位蓝点并不进行定位，默认是false。


        parentView = findViewById(R.id.root_view);

        savePath = findViewById(R.id.activity_main_save_path);
        screenShot = findViewById(R.id.activity_main_screen_shot);
        screenShot.setSelected(false);

        videostreamPreviewTtView = findViewById(R.id.livestream_preview_ttv);
        videostreamPreviewTtView.setClickable(true);

        videostreamPreviewSf = findViewById(R.id.livestream_preview_sf);
        videostreamPreviewSf.setClickable(true);
        videostreamPreviewSf.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onViewClick(videostreamPreviewSf);
            }
        });

        liveStream_switch = findViewById(R.id.live_stream_switch);
        updateUIVisibility();
    }

    private void addMarker(double lat, double lng) {
        Log.i("addMarker", lat + "____" + lng);
        //大疆DJIlatlng 转 高德latlng just for display the marker
        double[] dbs = GPSUtil.gps84_To_Gcj02(lat, lng);
        LatLng aMapClickPoint = new LatLng(dbs[0], dbs[1]);
        Waypoint mWaypoint = new Waypoint(lat, lng, altitude);
        b_point = new DJILatLng(lat, lng, altitude);

        LatLng aMapClickPoint2;
        Waypoint mWaypoint2;

        if (current_latlng != null) {
            aMapClickPoint2 = GPSUtil.gps84_To_Gcj02_ToLatlng(current_latlng.latitude, current_latlng.longitude);
            mWaypoint2 = new Waypoint(current_latlng.latitude, current_latlng.longitude, altitude);
            a_point = current_latlng;
        } else {
            aMapClickPoint2 = new LatLng(dbs[0] + 0.0001, dbs[1]);
            mWaypoint2 = new Waypoint(dbs[0] + 0.0001, dbs[1], altitude);
            a_point = new DJILatLng(dbs[0] + 0.0001, dbs[1], altitude);
        }

        if (Markers.size() > 0) {
            for (Marker marker : Markers) {
                marker.remove();

            }
            Markers.clear();
            waypointList.clear();
        }
        final Marker marker = aMap.addMarker(new MarkerOptions()
                .draggable(true)
                .position(aMapClickPoint)
                .title("")
                .snippet("DefaultMarker"));
        final Marker marker2 = aMap.addMarker(new MarkerOptions()
                .draggable(true)
                .position(aMapClickPoint2)
                .title("")
                .snippet("DefaultMarker"));
        Markers.add(marker);
        Markers.add(marker2);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                B_lat.setText(String.valueOf(lat));
                B_lng.setText(String.valueOf(lng));
            }
        });

        //Add Waypoints to Waypoint arraylist;
        if (waypointMissionBuilder != null) {
            waypointList.add(mWaypoint2);
            waypointList.add(mWaypoint);
            waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
        } else {
            waypointMissionBuilder = new WaypointMission.Builder();
            waypointList.add(mWaypoint2);
            waypointList.add(mWaypoint);
            waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
        }
        Log.e("waypointLIST size = ", String.valueOf(waypointList.size()));
    }

    /**
     * Waypoint数据
     */
    public void waypointTaskStart() {
        LinearLayout wayPointSettings = (LinearLayout) getLayoutInflater().inflate(R.layout.waypoint_setting_dialog, null);
        wayPointSettings.setMinimumWidth(400);

        final EditText wpAltitude_TV = wayPointSettings.findViewById(R.id.altitude);
        RadioGroup speed_RG = wayPointSettings.findViewById(R.id.speed);
        RadioGroup actionAfterFinished_RG = wayPointSettings.findViewById(R.id.actionAfterFinished);
        RadioGroup heading_RG = wayPointSettings.findViewById(R.id.heading);
        //设置初始值
        wpAltitude_TV.setText((int) altitude + "");

        if (mSpeed <= 3) {
            speed_RG.check(R.id.lowSpeed);
        } else if (3 < mSpeed && mSpeed <= 6) {
            speed_RG.check(R.id.MidSpeed);
        } else {
            speed_RG.check(R.id.HighSpeed);
        }
        speed_RG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if (checkedId == R.id.lowSpeed) {
                    mSpeed = 3.0f;
                } else if (checkedId == R.id.MidSpeed) {
                    mSpeed = 5.0f;
                } else if (checkedId == R.id.HighSpeed) {
                    mSpeed = 10.0f;
                }
            }
        });
        if (mFinishedAction == WaypointMissionFinishedAction.GO_HOME) {
            actionAfterFinished_RG.check(R.id.finishGoHome);
        } else if (mFinishedAction == WaypointMissionFinishedAction.AUTO_LAND) {
            actionAfterFinished_RG.check(R.id.finishAutoLanding);
        } else if (mFinishedAction == WaypointMissionFinishedAction.GO_FIRST_WAYPOINT) {
            actionAfterFinished_RG.check(R.id.finishToFirst);
        } else {
            actionAfterFinished_RG.check(R.id.finishNone);
        }
        actionAfterFinished_RG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                Log.d(TAG, "Select finish action");
                if (checkedId == R.id.finishNone) {
                    mFinishedAction = WaypointMissionFinishedAction.NO_ACTION;
                } else if (checkedId == R.id.finishGoHome) {
                    mFinishedAction = WaypointMissionFinishedAction.GO_HOME;
                } else if (checkedId == R.id.finishAutoLanding) {
//                    mFinishedAction = WaypointMissionFinishedAction.AUTO_LAND;
                } else if (checkedId == R.id.finishToFirst) {
                    mFinishedAction = WaypointMissionFinishedAction.GO_FIRST_WAYPOINT;
                }
            }
        });
        if (mHeadingMode == WaypointMissionHeadingMode.USING_INITIAL_DIRECTION) {
            heading_RG.check(R.id.headingInitDirec);
        } else if (mHeadingMode == WaypointMissionHeadingMode.CONTROL_BY_REMOTE_CONTROLLER) {
            heading_RG.check(R.id.headingRC);
        } else if (mHeadingMode == WaypointMissionHeadingMode.USING_WAYPOINT_HEADING) {
            heading_RG.check(R.id.headingWP);
        } else {
            heading_RG.check(R.id.headingNext);
        }
        heading_RG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                Log.d(TAG, "Select heading");
                //设置摄像头朝向
                if (checkedId == R.id.headingNext) {
                    mHeadingMode = WaypointMissionHeadingMode.AUTO;
                } else if (checkedId == R.id.headingInitDirec) {
                    mHeadingMode = WaypointMissionHeadingMode.USING_INITIAL_DIRECTION;
                } else if (checkedId == R.id.headingRC) {
                    mHeadingMode = WaypointMissionHeadingMode.CONTROL_BY_REMOTE_CONTROLLER;
                } else if (checkedId == R.id.headingWP) {
                    mHeadingMode = WaypointMissionHeadingMode.USING_WAYPOINT_HEADING;
                }
            }
        });
        new AlertDialog.Builder(this)
                .setTitle("")
                .setView(wayPointSettings)
                .setPositiveButton("完成", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                        String altitudeString = wpAltitude_TV.getText().toString();
                        altitude = Float.parseFloat(ToolUtil.nulltoIntegerDefalt(altitudeString));
                        configWayPointMission(false);
                    }
                })
                .setNegativeButton("取消", null)
                .create()
                .show();
    }

    //Add Listener for WaypointMissionOperator
    private void addListener() {
        if (getWaypointMissionOperator() != null) {
            getWaypointMissionOperator().addListener(eventNotificationListener);
        }

    }

    private void removeListener() {
        if (getWaypointMissionOperator() != null) {
            getWaypointMissionOperator().removeListener(eventNotificationListener);
        }
    }

    private Timer panoTaskTimer;

    enum PanActionStatus {
        NOT_EXECUTING,
        EXECUTING,
        FINISH_ACTION
    }

    private MediaUtil.MediaEventCallBack mediaEventCallBack = new MediaUtil.MediaEventCallBack() {
        @Override
        public void onEventStart() {
            runOnUiThread(() -> {
                mPanoTaskPanel.updatePanoStatus(shootcount, "开始保存");
            });
            Log.d("MediaEventCallBack", String.format("DownloadState: %s,", "start"));
        }

        @Override
        public void onEventUpdate(MediaEvent mediaEventState) {
            Log.d("MediaEventCallBack", String.format("DownloadState: %s, progress: %s",
                    mediaEventState.getDownloadState(),
                    mediaEventState.getProgress()
            ));
            runOnUiThread(() -> {
                mPanoTaskPanel.updateSaveProgress(shootcount, mediaEventState.getProgress());
                mPanoTaskPanel.updatePanoStatus(shootcount, "正在保存");
            });
        }

        @Override
        public void onEventSuccess(String id, String pic_path) {
            Log.d("MediaEventSuccess", String.format("id: %s, pic_path: %s", id, pic_path));
            runOnUiThread(() -> {
                mPanoTaskPanel.updateSavePath(shootcount, pic_path);
                mPanoTaskPanel.updatePanoStatus(shootcount, "保存成功");
            });
        }

        @Override
        public void onEventFailure(String error) {
            runOnUiThread(() -> {
                mPanoTaskPanel.updatePanoStatus(shootcount, "保存失败");
            });
            Log.e("MediaEventFailure", "onEventFailure: " + error);
        }
    };

    private MediaUploadUtil.OnUploadListener uploadListener = new MediaUploadUtil.OnUploadListener() {
        @Override
        public void onUploadSuccess() {
            runOnUiThread(() -> {
                mPanoTaskPanel.updatePanoStatus(shootcount, "上传成功");
            });
            Log.d("MediaUploadCallBack", "onUploadSuccess");
        }

        @Override
        public void onUploading(int progress) {
            runOnUiThread(() -> {
                mPanoTaskPanel.updateUploadProgress(shootcount, progress);
                mPanoTaskPanel.updatePanoStatus(shootcount, "正在上传");
            });
            Log.d("MediaUploadCallBack", "onUploading: " + progress);
        }

        @Override
        public void onUploadFailed(String e) {
            runOnUiThread(() -> mPanoTaskPanel.updatePanoStatus(shootcount, "上传失败"));
            Log.e("MediaUploadCallBack", "onUploadFailed: " + e);
        }
    };


    private PanActionStatus panActionStatus;
    private boolean isstartPano = false;
    private boolean isexPano = false;

    private final WaypointMissionOperatorListener eventNotificationListener = new WaypointMissionOperatorListener() {
        @Override
        public void onDownloadUpdate(WaypointMissionDownloadEvent downloadEvent) {

        }

        @Override
        public void onUploadUpdate(WaypointMissionUploadEvent uploadEvent) {
        }

        @Override
        public void onExecutionUpdate(WaypointMissionExecutionEvent executionEvent) {
            StringBuilder sb = new StringBuilder();
            if (executionEvent.getProgress() != null) {
                sb.append("PreviousState: ").append(executionEvent.getPreviousState()).append("\n")
                        .append("CurrentState: ").append(executionEvent.getCurrentState()).append("\n")
                        .append("Progress executeState: ").append(executionEvent.getProgress().executeState).append("\n")
                        .append("Progress totalWaypointCount: ").append(executionEvent.getProgress().totalWaypointCount).append("\n")
                        .append("Progress targetWaypointIndex: ").append(executionEvent.getProgress().targetWaypointIndex).append("\n")
                        .append("Progress isWaypointReached: ").append(executionEvent.getProgress().isWaypointReached).append("\n")
                        .append("Progress isStartPano: ").append(isstartPano).append("\n")
                        .append("Progress isExPano: ").append(isexPano).append("\n");

                Log.d("Media", String.format("onWaypointExecutionUpdate: \n %s", sb));
            }
        }

        @Override
        public void onExecutionStart() {
            //国土查违更新无人机状态为忙碌
            MediaUploadUtil.updateStatus(mRoomId, 3);
            isTakeOff = true;
            isMissionFinish = false;
//            startLiveShow(recordName);
//            try {
//                Thread.sleep(5000);
//                new Thread(() -> tcpDrone.sendCommand(PLCCommand.after_takeOff)).start();
//                Thread.sleep(5000);
//                new Thread(() -> tcpDrone.sendCommand(PLCCommand.plc_heart)).start();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
        }

        @Override
        public void onExecutionFinish(@Nullable final DJIError error) {
            if (error != null) {
                Log.d("wwww", "error: " + error.toString());
            }
            //国土
            else if (LandInspect) {
                //非返航航线 结束后拍照
                if (waypointcount != waypointList.size() - 1) {
//                    needupload = true;
                    new Thread(() -> {
                        try {
                            panoTaskTimer = new Timer();
                            panoTaskTimer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    StringBuilder sb1 = new StringBuilder();
                                    sb1.append("Progress isStartPano: ").append(isstartPano).append("\n")
                                            .append("Progress isExPano: ").append(isexPano).append("\n");
                                    Log.d("Media", String.format("onWaypointExecutionUpdate: \n %s", sb1));

                                    if (panActionStatus.equals(PanActionStatus.FINISH_ACTION) || panActionStatus.equals(PanActionStatus.NOT_EXECUTING)) {
                                        if (isstartPano && isexPano) {
                                            if (!getWaypointMissionOperator().getCurrentState().equals(WaypointMissionState.EXECUTING)) {
                                                waypointcount++;
                                                if (waypointcount != waypointList.size()) {
                                                    configWayPointMission(true);
                                                }
                                            }
                                            isexPano = false;
                                            isstartPano = false;
                                            panoTaskTimer.cancel();
                                        } else {
                                            if (!isstartPano) {
                                                DJISampleApplication.getProductInstance().getCamera().startShootPhoto(djiError -> {
                                                    if (djiError != null)
                                                        Log.e("Media", "startShootPhoto: " + djiError.toString());
                                                    else {
                                                        shootcount++;
                                                        isstartPano = true;
                                                        runOnUiThread(() -> {
                                                            logcatOnUI("startShootPanoPhoto");
                                                        });
                                                    }
                                                });
                                            }
                                        }
                                    } else if (panActionStatus.equals(PanActionStatus.EXECUTING)) {
                                        if (!isexPano) isexPano = true;
                                        Log.d("Media", "run: execute");
                                    }
                                }
                            }, 100, 1000);
                        } catch (Exception e) {
                            Log.e(TAG, String.format("onException: \n %s", e));
                        }
                    }).start();
                }
                //返航航线 结束 重置
                if (waypointcount == waypointList.size()) {
                    isLandReady = true;
                    isLanding = true;
                    isMissionFinish = true;
                    showToast("Execution finished: " + "Success!");
                    waypointList.clear();
                    if (waypointMissionBuilder != null) {
                        waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            start_waypoint_mission.setText("开始任务");
                        }
                    });
                    if (Markers.size() > 0) {
                        for (Marker marker : Markers) {
                            marker.remove();
                        }
                        Markers.clear();
                    }
                }
            } else {
                MediaUploadUtil.updateStatus(mRoomId, 2);
//                stopLiveShow();
//                mAISurfaceViewManager.clear();

            /*
            Camera camera = DJISampleApplication.getProductInstance().getCamera();
            camera.setMode(SettingsDefinitions.CameraMode.SHOOT_PHOTO,
                    djiError -> camera.startShootPhoto(djiError1 -> logcatOnUI("start shoot photo"))
            );*/
//                GimbalUtil.adjustGimbalPitch(-90);
                isLandReady = true;
                isLanding = true;
                isMissionFinish = true;
//            try {
//                Thread.sleep(5000);
//                new Thread(() -> tcpDrone.sendCommand(PLCCommand.ready_landing)).start();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }

                showToast("Execution finished: " + (error == null ? "Success!" : error.getDescription()));
                waypointList.clear();
                if (waypointMissionBuilder != null) {
                    waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
                }
//                try {
//                start_waypoint_mission.setText("开始任务");
//                enableVirtualStick();
//
//                startLiveShow(recordName+"-landing");
//                mSignalClient.sendMECSignal(mUserID, mRoomId, recordName);

//                    new Thread(new Runnable() {
//                        @Override
//                        public void run() {
//                            while (true) {
//                                if (havePLCStatus(PLCStatus.READY_PICKUP_OK) && mFinishedAction == WaypointMissionFinishedAction.GO_FIRST_WAYPOINT) {
//                                    start_waypoint_mission.setText("开始任务");
//                                    startLiveShow(recordName + "-landing");
//                                    enableVirtualStick();
//                                    break;
//                                }
//                            }
//                        }
//                    }).start();


//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
                if (Markers.size() > 0) {
                    for (Marker marker : Markers) {
                        marker.remove();
                    }
                    Markers.clear();
                }
            }
        }
    };


    /**
     * 设置waypoint飞行任务的参数
     */
    private void configWayPointMission(Boolean isLandInspect) {
        if (DJISampleApplication.getAircraftInstance() == null) return;
        LandInspect = isLandInspect;
        //国土
        if (isLandInspect) {
            swaypointList.clear();
            //任务航线
            if (waypointcount < waypointList.size() - 1) {
                swaypointList.add(waypointList.get(waypointcount));
                swaypointList.add(waypointList.get(waypointcount + 1));
            }
            //返航航线
            else if (waypointcount == waypointList.size() - 1) {
                swaypointList.add(waypointList.get(waypointcount));
                swaypointList.add(waypointList.get(0));
            }
            if (waypointMissionBuilder == null)
                waypointMissionBuilder = new WaypointMission.Builder();
            waypointMissionBuilder
                    //设定航点方向角
                    .headingMode(WaypointMissionHeadingMode.AUTO)
                    .autoFlightSpeed(mSpeed)
                    .maxFlightSpeed(mSpeed)
                    .flightPathMode(WaypointMissionFlightPathMode.NORMAL)
                    .setExitMissionOnRCSignalLostEnabled(true);
            //返航航线 结束动作自动着陆
            if (waypointcount == waypointList.size() - 1)
                waypointMissionBuilder.finishedAction(WaypointMissionFinishedAction.GO_HOME);
            else
                waypointMissionBuilder.finishedAction(WaypointMissionFinishedAction.NO_ACTION);
            waypointMissionBuilder.waypointList(swaypointList).waypointCount(2);
        } else {
            if (waypointMissionBuilder == null)
                waypointMissionBuilder = new WaypointMission.Builder();
            waypointMissionBuilder.finishedAction(mFinishedAction)
                    .headingMode(mHeadingMode)
                    .autoFlightSpeed(mSpeed)
                    .maxFlightSpeed(mSpeed)
                    .flightPathMode(WaypointMissionFlightPathMode.NORMAL);
            waypointMissionBuilder.waypointList(waypointList).waypointCount(waypointList.size());
        }
        if (waypointMissionBuilder.getWaypointList().size() > 0) {
            logcatOnUI("wplistSize" + waypointMissionBuilder.getWaypointList().size());
            //国土
            if (isLandInspect) {
                //航点设置
                if (waypointcount != waypointList.size() - 1) {
                    waypointMissionBuilder.getWaypointList().get(1).altitude = altitude;
                    waypointMissionBuilder.getWaypointList().get(1).heading = 0;
                    waypointMissionBuilder.getWaypointList().get(1).addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, 0));
                }
                //返回起飞点
                if (waypointcount == waypointList.size() - 1)
                    waypointMissionBuilder.getWaypointList().get(1).addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, -90));
            } else {
                for (int i = 0; i < waypointMissionBuilder.getWaypointList().size(); i++) {
                    waypointMissionBuilder.getWaypointList().get(i).altitude = altitude;
                    waypointMissionBuilder.getWaypointList().get(i).heading = 0;

//                    if (isLandInspect) {
//                        waypointMissionBuilder.getWaypointList().get(i).addAction(new WaypointAction(WaypointActionType.GIMBAL_PITCH, -90));
//                        waypointMissionBuilder.getWaypointList().get(i).addAction(new WaypointAction(WaypointActionType.START_TAKE_PHOTO, 1));
//                    }
                }
            }
        }
        Log.e("loadMissionState", waypointMissionOperator.getCurrentState().getName());
        DJIError error = getWaypointMissionOperator().loadMission(waypointMissionBuilder.build());
        if (error == null) {
            logcatOnUI("loadWaypoint succeeded");
            //无法调用精准起飞 卡住全流程 暂注释掉
            //不执行也不报错 方法里第一句写log都打印不出来 未知原因
//                mvirtualSticksController.Aircraft_PrecisionTakeoff((float) fxj_heading_cur);
            VirtualSticksController.landHeading = (float) fxj_heading_cur;
            uploadWaypointMission();
        } else {
            showToast("loadWaypoint failed " + error.getDescription());
        }
    }

    /**
     * Add Listener for WaypointMissionOperator（飞行轨迹）
     *
     * @return
     */
    public WaypointMissionOperator getWaypointMissionOperator() {
        if (waypointMissionOperator == null) {
            waypointMissionOperator = MissionControl.getInstance().getWaypointMissionOperator();
        }
        return waypointMissionOperator;
    }

    private void updateUIVisibility() {
        /**
         * we need display two video stream at the same time, so we need let them to be visible.
         */
        videostreamPreviewSf.setVisibility(View.VISIBLE);
        videostreamPreviewTtView.setVisibility(View.VISIBLE);

    }

    private void initListener() {
        listener = new LiveStreamManager.OnLiveChangeListener() {
            @Override
            public void onStatusChanged(int i) {
                ToastUtils.showShort(CompleteWidgetActivity.this, "status changed : " + i);
            }
        };
        addListener();
    }

    @Override
    public void onConnectSuccess() {
        Log.i(TAG, "***********连接成功");
        ToastUtils.showShort(CompleteWidgetActivity.this, "Websocket连接成功");
    }

    @Override
    public void onConnectFailed() {
        Log.i(TAG, "***********连接失败");
        ToastUtils.showShort(CompleteWidgetActivity.this, "Websocket连接失败");

    }

    @Override
    public void onClose() {
//        mvirtualSticksController.closeVirtualStickController();
        Log.i(TAG, "***********关闭成功");

    }

    @Override
    public void onMessage(String text) {
        Log.i(TAG, "***********接收信息：" + text);

    }

    @Override
    public void onYuvDataReceived(MediaFormat format, final ByteBuffer yuvFrame, int dataSize, final int width, final int height) {
        //In this demo, we test the YUV data by saving it into JPG files.
        DJILog.d(TAG, "onYuvDataReceived " + dataSize);
        if (count++ % 100 == 0 && yuvFrame != null) {
            final byte[] bytes = new byte[dataSize];
            yuvFrame.get(bytes);
            Log.i(TAG, "***********onYuvDataReceived2：" + width + "-" + height);
            DJILog.d(TAG, "onYuvDataReceived2 " + dataSize, width, height);
            AsyncTask.execute(new Runnable() {
                @Override
                public void run() {
                    if (Build.VERSION.SDK_INT <= 23) {
                        oldSaveYuvDataToJPEG(bytes, width, height);
                    } else {
                        newSaveYuvDataToJPEG(bytes, width, height);
                    }
                }
            });
        }
    }

    // For android API <= 23
    private void oldSaveYuvDataToJPEG(byte[] yuvFrame, int width, int height) {
        if (yuvFrame.length < width * height) {
            DJILog.d(TAG, "yuvFrame size is too small " + yuvFrame.length);
            return;
        }

        byte[] y = new byte[width * height];
        byte[] u = new byte[width * height / 4];
        byte[] v = new byte[width * height / 4];
        byte[] nu = new byte[width * height / 4]; //
        byte[] nv = new byte[width * height / 4];

        System.arraycopy(yuvFrame, 0, y, 0, y.length);
        for (int i = 0; i < u.length; i++) {
            v[i] = yuvFrame[y.length + 2 * i];
            u[i] = yuvFrame[y.length + 2 * i + 1];
        }
        int uvWidth = width / 2;
        int uvHeight = height / 2;
        for (int j = 0; j < uvWidth / 2; j++) {
            for (int i = 0; i < uvHeight / 2; i++) {
                byte uSample1 = u[i * uvWidth + j];
                byte uSample2 = u[i * uvWidth + j + uvWidth / 2];
                byte vSample1 = v[(i + uvHeight / 2) * uvWidth + j];
                byte vSample2 = v[(i + uvHeight / 2) * uvWidth + j + uvWidth / 2];
                nu[2 * (i * uvWidth + j)] = uSample1;
                nu[2 * (i * uvWidth + j) + 1] = uSample1;
                nu[2 * (i * uvWidth + j) + uvWidth] = uSample2;
                nu[2 * (i * uvWidth + j) + 1 + uvWidth] = uSample2;
                nv[2 * (i * uvWidth + j)] = vSample1;
                nv[2 * (i * uvWidth + j) + 1] = vSample1;
                nv[2 * (i * uvWidth + j) + uvWidth] = vSample2;
                nv[2 * (i * uvWidth + j) + 1 + uvWidth] = vSample2;
            }
        }
        //nv21test
        byte[] bytes = new byte[yuvFrame.length];
        System.arraycopy(y, 0, bytes, 0, y.length);
        for (int i = 0; i < u.length; i++) {
            bytes[y.length + (i * 2)] = nv[i];
            bytes[y.length + (i * 2) + 1] = nu[i];
        }
        Log.d(TAG,
                "onYuvDataReceived: frame index: "
                        + DJIVideoStreamDecoder.getInstance().frameIndex
                        + ",array length: "
                        + bytes.length);
//        screenShot(bytes, Environment.getExternalStorageDirectory() + "/DJI_ScreenShot", width, height);
        sendImageBytes(bytes, width, height);
    }

    private void newSaveYuvDataToJPEG(byte[] yuvFrame, int width, int height) {
        if (yuvFrame.length < width * height) {
            DJILog.d(TAG, "yuvFrame size is too small " + yuvFrame.length);
            return;
        }
        int length = width * height;

        byte[] u = new byte[width * height / 4];
        byte[] v = new byte[width * height / 4];
        for (int i = 0; i < u.length; i++) {
            v[i] = yuvFrame[length + 2 * i];
            u[i] = yuvFrame[length + 2 * i + 1];
        }
        for (int i = 0; i < u.length; i++) {
            yuvFrame[length + 2 * i] = u[i];
            yuvFrame[length + 2 * i + 1] = v[i];
        }
//        screenShot(yuvFrame,Environment.getExternalStorageDirectory() + "/DJI_ScreenShot", width, height);
        sendImageBytes(yuvFrame, width, height);
    }

    private void sendImageBytes(byte[] buf, int width, int height) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        YuvImage yuvImage = new YuvImage(buf,
                ImageFormat.NV21,
                width,
                height,
                null);
        yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, stream);
        ByteString byteString = ByteString.of(stream.toByteArray());
        try {
            JSONObject args = new JSONObject();
            args.put("cmd", "ABTestHeight");
            args.put("currHeight", current_latlng.altitude);
            if (WebSocketManager.getInstance().isConnect()) {
                WebSocketManager.getInstance().sendMessage(args.toString());
                WebSocketManager.getInstance().sendMessage(byteString);
            }
        } catch (Exception e) {
            Log.e("sendMessageError", e.toString());
            e.printStackTrace();
        }
    }

    public void enableVirtualStick() {
        Button vt_btn = findViewById(R.id.endisable_virturalSticks);
        mvirtualSticksController.enableVirtualSticks();
        vt_btn.setText("关闭摇杆");
    }

    public void disableVirtualStick() {
        Button vt_btn = findViewById(R.id.endisable_virturalSticks);
        mvirtualSticksController.disableVirtualSticks();
        vt_btn.setText("开启摇杆");
    }

    @SuppressLint("NonConstantResourceId")
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.show_logcat:
                if (mLogcatView.getVisibility() != View.VISIBLE)
                    mLogcatView.setVisibility(View.VISIBLE);
                else mLogcatView.setVisibility(View.INVISIBLE);
                break;
            case R.id.show_pano_progress:
                if (mPanoTaskPanel.getVisibility() != View.VISIBLE)
                    mPanoTaskPanel.setVisibility(View.VISIBLE);
                else mPanoTaskPanel.setVisibility(View.INVISIBLE);
                break;
            case R.id.activity_main_screen_shot:
                try {
                    DJISampleApplication.getCameraInstance().setFocusMode(SettingsDefinitions.FocusMode.AUTO, new CommonCallbacks.CompletionCallback() {
                        @Override
                        public void onResult(DJIError djiError) {
                            if (djiError != null)
                                Log.d(TAG, "setFocusModeResult: " + djiError.toString());
                        }
                    });
                    DJISampleApplication.getCameraInstance().setFocusTarget(new PointF(0.5f, 0.5f), new CommonCallbacks.CompletionCallback() {
                        @Override
                        public void onResult(DJIError djiError) {
                            if (djiError != null)
                                Log.d(TAG, "setFocusTargetResult: " + djiError.toString());
                            Log.d(TAG, "setFocusTargetSuccess");
                        }
                    });
                    DJISampleApplication.getCameraInstance().getLensInformation(new CommonCallbacks.CompletionCallbackWith<String>() {
                        @Override
                        public void onSuccess(String s) {
                            Log.d(TAG, "getLensInformation onSuccess: " + s);
                        }

                        @Override
                        public void onFailure(DJIError djiError) {
                            Log.e(TAG, "getLensInformation onFailure: " + djiError);
                        }
                    });
                    List<Lens> lenList = DJISampleApplication.getCameraInstance().getLenses();
                    Log.d(TAG, "getLenses Success: " + (lenList != null ? lenList.toString() : "null"));
                    DJISampleApplication.getCameraInstance().getHybridZoomFocalLength(new CommonCallbacks.CompletionCallbackWith<Integer>() {
                        @Override
                        public void onSuccess(Integer integer) {
                            Log.d(TAG, "getHybridZoomFocalLength onSuccess: " + integer);
                        }

                        @Override
                        public void onFailure(DJIError djiError) {
                            Log.e(TAG, "getHybridZoomFocalLength onFailure: " + djiError);
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            case R.id.endisable_virturalSticks:
                Button vt_btn = findViewById(R.id.endisable_virturalSticks);
                if (vt_btn.getText().equals("开启摇杆")) {
                    mvirtualSticksController.enableVirtualSticks();
                    vt_btn.setText("关闭摇杆");
                } else {
                    mvirtualSticksController.disableVirtualSticks();
                    vt_btn.setText("开启摇杆");
                }
//                mvirtualSticksController.disableVirtualSticks();
                break;
            case R.id.docall:
                Button webRTC_call_btn = findViewById(R.id.docall);
                if (webRTC_call_btn.getText().equals("开始通话")) {
                    onClickStartCallButton();
                    //前端测试
//                    JSONObject args = new JSONObject();
//                    try {
//                        args.put("readyTakeOffOK", havePLCStatus(PLCStatus.HAVE_DRONE, PLCStatus.READY_TAKEOFF_OK));
//                        args.put("weatherParams", JSON.toJSONString(new WeatherParams(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)));
//                    } catch (JSONException e) {
//                        e.printStackTrace();
//                    }
//                    Timer timer = new Timer();
//                    timer.schedule(new TimerTask() {
//                        @Override
//                        public void run() {
//                            mSignalClient.sendFlightdata(args.toString(), mRemoteUserId);
//                        }
//                    }, 1000, 1000);
                    webRTC_call_btn.setText("结束通话");
                } else {
                    onClickEndCallButton();
                    webRTC_call_btn.setText("开始通话");
                }
                break;
            case R.id.set_rtmp_add:
                SetLiveStreamAdd_dialog();
                break;
            case R.id.live_stream_switch:
                if (!isLiveStreamManagerOn()) {
                    return;
                }
                if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
                    stopLiveShow();
                    liveStream_switch.setText("开始推流");
                } else {
                    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
                    Date date = new Date(System.currentTimeMillis());
                    String curTime = formatter.format(date);
                    startLiveShow(curTime);
                    liveStream_switch.setText("停止推流");
                }
                break;
                /*
            case R.id.confim_B_point:
                addMarker(Double.parseDouble(String.valueOf(B_lat.getText())), Double.parseDouble(String.valueOf(B_lng.getText())));
                break;
            case R.id.waypoint_conf:
                waypointTaskStart();
                break;

                 */
            case R.id.urLand_conf:
                if (murgencyLandController != null)
                    runOnUiThread(() -> murgencyLandController.UrgencyLandConfigDialog());
                break;
            case R.id.start_waypoint_mission:
                if (!isStartWaypoint) {
                    uploadWaypointMission();
                    isStartWaypoint = true;
                    start_waypoint_mission.setText("停止任务");
                } else {
                    stopWaypointMission();
                    isStartWaypoint = false;
                    start_waypoint_mission.setText("开始任务");
                }
                break;
            case R.id.addwaypoint:
//                if (isAdd == false) {
//                    isAdd = true;
//                    addwaypoint_btn.setText("Exit");
//                } else {
//                    isAdd = false;
//                    addwaypoint_btn.setText("Add");
//                }
//                mvirtualSticksController.Aircraft_PrecisionTakeoff((float) fxj_heading_cur);

                DJISampleApplication.getProductInstance().getCamera().startShootPhoto(new CommonCallbacks.CompletionCallback() {
                    @Override
                    public void onResult(DJIError djiError) {
                        if (djiError != null)
                            Log.d("wwww", "startShootPhoto: " + djiError.toString());
                    }
                });
                break;
            case R.id.set_KPID:
                if (mvirtualSticksController != null) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mvirtualSticksController.setKPID_Dialog();
                        }
                    });
                }
                break;
            case R.id.stickpanorama:
                VirtualStickPanoramaController vspController = new VirtualStickPanoramaController(CompleteWidgetActivity.this, mFlightController);
                vspController.shootPanorama();
                break;
            default:
                break;
        }
    }

    private boolean isStartWaypoint = false;

    private void uploadWaypointMission() {
        logcatOnUI("uploadWaypointMission");
        Log.e(TAG, "waypointmissionState: " + getWaypointMissionOperator().getCurrentState());
        if (WaypointMissionState.READY_TO_RETRY_UPLOAD.equals(getWaypointMissionOperator().getCurrentState())
                || WaypointMissionState.READY_TO_UPLOAD.equals(getWaypointMissionOperator().getCurrentState())) {
            //upload数据
            getWaypointMissionOperator().uploadMission(new CommonCallbacks.CompletionCallback() {
                @Override
                public void onResult(DJIError error) {
                    if (error == null) {
                        logcatOnUI("mission upload success");
                        isMissionReady = true;
                        startWaypointMission();
                    } else {
                        showToast("任务上传失败, error: " + error + " retrying...");
                        getWaypointMissionOperator().retryUploadMission(new CommonCallbacks.CompletionCallback() {
                            @Override
                            public void onResult(DJIError djiError) {
                                showToast("重试上传结果error: " + djiError.getDescription() + " retrying...");
                            }
                        });
                    }
                }

            });
        } else {
            showToast("Not ready to upload!");
            logcatOnUI("Not ready to upload!");
        }

    }

    /**
     * 确认开始航点飞行的对话框
     */
    private void showStartWaypointDialog() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                new AlertDialog.Builder(CompleteWidgetActivity.this)
                        .setTitle("WaypointMission")
                        .setMessage("任务已经成功上传，是否开始航点飞行?")
                        .setPositiveButton("是", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                startWaypointMission();
                            }
                        })
                        .setNegativeButton("否", null)
                        .create()
                        .show();
            }
        });
    }

    private void startWaypointMission() {
        Log.d(TAG, "startWaypointMission");
        logcatOnUI("WaypointMission start");
        isMissionReady = false;
        if (waypointMissionBuilder.build() != null) {
            getWaypointMissionOperator().startMission(new CommonCallbacks.CompletionCallback() {
                @Override
                public void onResult(DJIError error) {
                    if (error != null) {
                        Log.d(TAG, "startWaypointMission onResult: " + error.getDescription() + "waypointList:" + waypointList.size());
                        showToast("Mission Start: Error " + error.getDescription() + "waypointList:" + waypointList.size());
                    } else {
                        logcatOnUI("start WaypointMission success");
                        isStartWaypoint = true;
                        start_waypoint_mission.setText("停止任务");
                    }
                }
            });
        } else {
            showToast("Prepare Mission First!");
        }
    }

    private void stopWaypointMission() {
        if (getWaypointMissionOperator() == null) {
            return;
        }
        if (WaypointMissionState.EXECUTION_STARTING.equals(getWaypointMissionOperator().getCurrentState())
                || WaypointMissionState.EXECUTION_PAUSING.equals(getWaypointMissionOperator().getCurrentState())
                || WaypointMissionState.EXECUTING.equals(getWaypointMissionOperator().getCurrentState())
                || WaypointMissionState.EXECUTION_RESUMING.equals(getWaypointMissionOperator().getCurrentState())) {
            getWaypointMissionOperator().stopMission(new CommonCallbacks.CompletionCallback() {
                @Override
                public void onResult(DJIError error) {
                    if (error == null) {
                        isStartWaypoint = false;
                        start_waypoint_mission.setText("开始任务");
                    }
                    showToast("Mission Stop: " + (error == null ? "Successfully" : error.getDescription()));
                }
            });
        }
    }


    private class ResizeAnimation extends Animation {

        private View mView;
        private int mToHeight;
        private int mFromHeight;

        private int mToWidth;
        private int mFromWidth;
        private int mMargin;

        private ResizeAnimation(View v, int fromWidth, int fromHeight, int toWidth, int toHeight, int margin) {
            mToHeight = toHeight;
            mToWidth = toWidth;
            mFromHeight = fromHeight;
            mFromWidth = fromWidth;
            mView = v;
            mMargin = margin;
            setDuration(300);
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            float height = (mToHeight - mFromHeight) * interpolatedTime + mFromHeight;
            float width = (mToWidth - mFromWidth) * interpolatedTime + mFromWidth;
            RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) mView.getLayoutParams();
            p.height = (int) height;
            p.width = (int) width;
            p.rightMargin = mMargin;
            p.bottomMargin = mMargin;
            mView.requestLayout();
        }
    }

    private void handleYUVClick() {
        if (screenShot.isSelected()) {
            screenShot.setText("YUV Screen Shot");
            screenShot.setSelected(false);

            DJIVideoStreamDecoder.getInstance().changeSurface(videostreamPreviewSh.getSurface());
            DJIVideoStreamDecoder.getInstance().setYuvDataListener(null);

//            savePath.setText("");
//            savePath.setVisibility(View.INVISIBLE);
            stringBuilder = null;
        } else {
            screenShot.setText("Live Stream");
            screenShot.setSelected(true);
            DJIVideoStreamDecoder.getInstance().changeSurface(null);
            DJIVideoStreamDecoder.getInstance().setYuvDataListener(CompleteWidgetActivity.this);

//            savePath.setText("");
//            savePath.setVisibility(View.VISIBLE);
        }
    }

    private void displayPath(String path) {
        if (stringBuilder == null) {
            stringBuilder = new StringBuilder();
        }

        path = path + "\n";
        stringBuilder.append(path);
        savePath.setText(stringBuilder.toString());
    }


    public void showToast(final String msg) {
        runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(CompleteWidgetActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
        });
    }

    public void initTCPClient() {
        Log.d(TAG, "initTCPClient");
        tcpDrone = new TCPDrone("192.168.2.250", 20000);
        tcpDrone.setPLCStatusCallBack(new TCPDrone.OnPLCStatusCallBackBlock() {
            @Override
            public void callback(ArrayList<PLCStatus> receicedMessage) {
                plcStatusesList = (ArrayList<PLCStatus>) receicedMessage.clone();
                Log.d(TAG, "PLCStatusCallBack: " + plcStatusesList.toString());
            }
        });
        tcpDrone.setWeatherParamsCallBack(new TCPDrone.OnWeatherParamsCallBack() {
            @Override
            public void callback(WeatherParams param) {
                weatherParams = param;
                Log.d(TAG, "WeatherParamCallBack: " + weatherParams.toString());
            }
        });


    }
}
